github.com/jxskiss/gopkg/v2@v2.14.9-0.20240514120614-899f3e7952b4/encrypt/obscure/obscure.go (about)

     1  package obscure
     2  
     3  import (
     4  	"crypto/md5"
     5  	"encoding/base32"
     6  	"encoding/binary"
     7  	"errors"
     8  
     9  	"github.com/jxskiss/gopkg/v2/internal/unsafeheader"
    10  	"github.com/jxskiss/gopkg/v2/perf/fastrand"
    11  )
    12  
    13  const (
    14  	idxLen  = 61
    15  	encBase = 32
    16  	chars62 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
    17  )
    18  
    19  var ErrInvalidInput = errors.New("obscure: invalid input")
    20  
    21  var binEnc = binary.BigEndian
    22  
    23  func getRandomChars(r *fastrand.Rand, dst []byte) {
    24  	chars := []byte(chars62)
    25  	r.Shuffle(len(chars), func(i, j int) {
    26  		chars[i], chars[j] = chars[j], chars[i]
    27  	})
    28  	copy(dst, chars)
    29  }
    30  
    31  func fnvHash32(buf []byte) uint32 {
    32  	const offset32 = 2166136261
    33  	const prime32 = 16777619
    34  
    35  	var hash uint32 = offset32
    36  	for _, c := range buf {
    37  		hash *= prime32
    38  		hash ^= uint32(c)
    39  	}
    40  	return hash
    41  }
    42  
    43  type Obscure struct {
    44  	idxChars  [idxLen]byte
    45  	idxDec    [128]int
    46  	table     [idxLen][encBase]byte
    47  	encodings [idxLen]*base32.Encoding
    48  }
    49  
    50  func New(key []byte) *Obscure {
    51  	hash := md5.Sum(key)
    52  	hi, lo := binEnc.Uint64(hash[:8]), binEnc.Uint64(hash[8:16])
    53  	rand := fastrand.NewPCG(hi, lo)
    54  	obs := &Obscure{}
    55  	getRandomChars(rand, obs.idxChars[:])
    56  	for i := 0; i < idxLen; i++ {
    57  		obs.idxDec[obs.idxChars[i]] = i
    58  		getRandomChars(rand, obs.table[i][:])
    59  		obs.encodings[i] = base32.NewEncoding(string(obs.table[i][:])).WithPadding(base32.NoPadding)
    60  	}
    61  	return obs
    62  }
    63  
    64  func (p *Obscure) Index() string {
    65  	return string(p.idxChars[:])
    66  }
    67  
    68  func (p *Obscure) Table() [61]string {
    69  	var out [61]string
    70  	for i := 0; i < idxLen; i++ {
    71  		out[i] = string(p.table[i][:])
    72  	}
    73  	return out
    74  }
    75  
    76  func (p *Obscure) EncodedLen(n int) int {
    77  	if n <= 0 {
    78  		return 0
    79  	}
    80  	return 1 + p.encodings[0].EncodedLen(n)
    81  }
    82  
    83  func (p *Obscure) Encode(dst, src []byte) {
    84  	if len(src) == 0 {
    85  		return
    86  	}
    87  	idx := fnvHash32(middle(src)) % idxLen
    88  	idxChar := p.idxChars[idx]
    89  	enc := p.encodings[idx]
    90  	dst[0] = idxChar
    91  	enc.Encode(dst[1:], src)
    92  }
    93  
    94  func middle(b []byte) []byte {
    95  	if len(b) > 200 {
    96  		x := len(b) / 2
    97  		return b[x : x+200]
    98  	}
    99  	return b
   100  }
   101  
   102  func (p *Obscure) EncodeToBytes(src []byte) []byte {
   103  	if len(src) == 0 {
   104  		return nil
   105  	}
   106  	dst := make([]byte, p.EncodedLen(len(src)))
   107  	p.Encode(dst, src)
   108  	return dst
   109  }
   110  
   111  func (p *Obscure) EncodeToString(src []byte) string {
   112  	if len(src) == 0 {
   113  		return ""
   114  	}
   115  	dst := p.EncodeToBytes(src)
   116  	return unsafeheader.BytesToString(dst)
   117  }
   118  
   119  func (p *Obscure) DecodedLen(n int) int {
   120  	if n <= 1 {
   121  		return 0
   122  	}
   123  	return p.encodings[0].DecodedLen(n - 1)
   124  }
   125  
   126  func (p *Obscure) Decode(dst, src []byte) (n int, err error) {
   127  	if len(src) == 0 {
   128  		return 0, nil
   129  	}
   130  	idxchar := src[0]
   131  	if idxchar >= 128 {
   132  		return 0, ErrInvalidInput
   133  	}
   134  	idx := p.idxDec[idxchar]
   135  	if idx == 0 && idxchar != p.idxChars[0] {
   136  		return 0, ErrInvalidInput
   137  	}
   138  	enc := p.encodings[idx]
   139  	return enc.Decode(dst, src[1:])
   140  }
   141  
   142  func (p *Obscure) DecodeBytes(src []byte) ([]byte, error) {
   143  	if len(src) == 0 {
   144  		return nil, nil
   145  	}
   146  	dst := make([]byte, p.DecodedLen(len(src)))
   147  	n, err := p.Decode(dst, src)
   148  	if err != nil {
   149  		return nil, err
   150  	}
   151  	return dst[:n], nil
   152  }
   153  
   154  func (p *Obscure) DecodeString(src string) ([]byte, error) {
   155  	buf := unsafeheader.StringToBytes(src)
   156  	return p.DecodeBytes(buf)
   157  }