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 }