github.com/bodgit/sevenzip@v1.5.1/internal/aes7z/key.go (about) 1 package aes7z 2 3 import ( 4 "bytes" 5 "crypto/sha256" 6 "encoding/binary" 7 "encoding/hex" 8 9 lru "github.com/hashicorp/golang-lru/v2" 10 "go4.org/syncutil" 11 "golang.org/x/text/encoding/unicode" 12 "golang.org/x/text/transform" 13 ) 14 15 type cacheKey struct { 16 password string 17 cycles int 18 salt string // []byte isn't comparable 19 } 20 21 const cacheSize = 10 22 23 //nolint:gochecknoglobals 24 var ( 25 once syncutil.Once 26 cache *lru.Cache[cacheKey, []byte] 27 ) 28 29 func calculateKey(password string, cycles int, salt []byte) ([]byte, error) { 30 if err := once.Do(func() (err error) { 31 cache, err = lru.New[cacheKey, []byte](cacheSize) 32 33 return 34 }); err != nil { 35 return nil, err 36 } 37 38 ck := cacheKey{ 39 password: password, 40 cycles: cycles, 41 salt: hex.EncodeToString(salt), 42 } 43 44 if key, ok := cache.Get(ck); ok { 45 return key, nil 46 } 47 48 b := bytes.NewBuffer(salt) 49 50 // Convert password to UTF-16LE 51 utf16le := unicode.UTF16(unicode.LittleEndian, unicode.IgnoreBOM) 52 t := transform.NewWriter(b, utf16le.NewEncoder()) 53 _, _ = t.Write([]byte(password)) 54 55 key := make([]byte, sha256.Size) 56 if cycles == 0x3f { 57 copy(key, b.Bytes()) 58 } else { 59 h := sha256.New() 60 for i := uint64(0); i < 1<<cycles; i++ { 61 // These will never error 62 _, _ = h.Write(b.Bytes()) 63 _ = binary.Write(h, binary.LittleEndian, i) 64 } 65 copy(key, h.Sum(nil)) 66 } 67 68 _ = cache.Add(ck, key) 69 70 return key, nil 71 }