github.com/bodgit/sevenzip@v1.5.1/internal/aes7z/reader.go (about) 1 package aes7z 2 3 import ( 4 "bytes" 5 "crypto/aes" 6 "crypto/cipher" 7 "errors" 8 "io" 9 ) 10 11 var errProperties = errors.New("aes7z: not enough properties") 12 13 type readCloser struct { 14 rc io.ReadCloser 15 salt, iv []byte 16 cycles int 17 cbc cipher.BlockMode 18 buf bytes.Buffer 19 } 20 21 func (rc *readCloser) Close() error { 22 var err error 23 if rc.rc != nil { 24 err = rc.rc.Close() 25 rc.rc = nil 26 } 27 28 return err 29 } 30 31 func (rc *readCloser) Password(p string) error { 32 key, err := calculateKey(p, rc.cycles, rc.salt) 33 if err != nil { 34 return err 35 } 36 37 block, err := aes.NewCipher(key) 38 if err != nil { 39 return err 40 } 41 42 rc.cbc = cipher.NewCBCDecrypter(block, rc.iv) 43 44 return nil 45 } 46 47 func (rc *readCloser) Read(p []byte) (int, error) { 48 if rc.rc == nil { 49 return 0, errors.New("aes7z: Read after Close") 50 } 51 52 if rc.cbc == nil { 53 return 0, errors.New("aes7z: no password set") 54 } 55 56 var block [aes.BlockSize]byte 57 58 for rc.buf.Len() < len(p) { 59 if _, err := io.ReadFull(rc.rc, block[:]); err != nil { 60 if errors.Is(err, io.EOF) { 61 break 62 } 63 64 return 0, err 65 } 66 67 rc.cbc.CryptBlocks(block[:], block[:]) 68 69 _, _ = rc.buf.Write(block[:]) 70 } 71 72 return rc.buf.Read(p) 73 } 74 75 // NewReader returns a new AES-256-CBC & SHA-256 io.ReadCloser. The Password 76 // method must be called before attempting to call Read so that the block 77 // cipher is correctly initialised. 78 func NewReader(p []byte, _ uint64, readers []io.ReadCloser) (io.ReadCloser, error) { 79 if len(readers) != 1 { 80 return nil, errors.New("aes7z: need exactly one reader") 81 } 82 83 // Need at least two bytes initially 84 if len(p) < 2 { 85 return nil, errProperties 86 } 87 88 if p[0]&0xc0 == 0 { 89 return nil, errors.New("aes7z: unsupported compression method") 90 } 91 92 rc := new(readCloser) 93 94 salt := p[0]>>7&1 + p[1]>>4 95 iv := p[0]>>6&1 + p[1]&0x0f 96 97 if len(p) != int(2+salt+iv) { 98 return nil, errProperties 99 } 100 101 rc.salt = p[2 : 2+salt] 102 rc.iv = make([]byte, aes.BlockSize) 103 copy(rc.iv, p[2+salt:]) 104 105 rc.cycles = int(p[0] & 0x3f) 106 rc.rc = readers[0] 107 108 return rc, nil 109 }