github.com/blend/go-sdk@v1.20220411.3/crypto/stream.go (about) 1 /* 2 3 Copyright (c) 2022 - Present. Blend Labs, Inc. All rights reserved 4 Use of this source code is governed by a MIT license that can be found in the LICENSE file. 5 6 */ 7 8 package crypto 9 10 import ( 11 "crypto/aes" 12 "crypto/cipher" 13 "crypto/hmac" 14 "crypto/rand" 15 "crypto/sha256" 16 "hash" 17 "io" 18 19 "github.com/blend/go-sdk/ex" 20 ) 21 22 // NewStreamEncrypter creates a new stream encrypter 23 func NewStreamEncrypter(encKey, macKey []byte, plainText io.Reader) (*StreamEncrypter, error) { 24 block, err := aes.NewCipher(encKey) 25 if err != nil { 26 return nil, ex.New(err) 27 } 28 iv := make([]byte, block.BlockSize()) 29 _, err = rand.Read(iv) 30 if err != nil { 31 return nil, ex.New(err) 32 } 33 stream := cipher.NewCTR(block, iv) 34 mac := hmac.New(sha256.New, macKey) 35 return &StreamEncrypter{ 36 Source: plainText, 37 Block: block, 38 Stream: stream, 39 Mac: mac, 40 IV: iv, 41 }, nil 42 } 43 44 // NewStreamDecrypter creates a new stream decrypter 45 func NewStreamDecrypter(encKey, macKey []byte, meta StreamMeta, cipherText io.Reader) (*StreamDecrypter, error) { 46 block, err := aes.NewCipher(encKey) 47 if err != nil { 48 return nil, ex.New(err) 49 } 50 stream := cipher.NewCTR(block, meta.IV) 51 mac := hmac.New(sha256.New, macKey) 52 return &StreamDecrypter{ 53 Source: cipherText, 54 Block: block, 55 Stream: stream, 56 Mac: mac, 57 Meta: meta, 58 }, nil 59 } 60 61 // StreamEncrypter is an encrypter for a stream of data with authentication 62 type StreamEncrypter struct { 63 Source io.Reader 64 Block cipher.Block 65 Stream cipher.Stream 66 Mac hash.Hash 67 IV []byte 68 } 69 70 // StreamDecrypter is a decrypter for a stream of data with authentication 71 type StreamDecrypter struct { 72 Source io.Reader 73 Block cipher.Block 74 Stream cipher.Stream 75 Mac hash.Hash 76 Meta StreamMeta 77 } 78 79 // Read encrypts the bytes of the inner reader and places them into p 80 func (s *StreamEncrypter) Read(p []byte) (int, error) { 81 n, readErr := s.Source.Read(p) 82 if n > 0 { 83 s.Stream.XORKeyStream(p[:n], p[:n]) 84 err := writeHash(s.Mac, p[:n]) 85 if err != nil { 86 return n, ex.New(err) 87 } 88 return n, readErr 89 } 90 return 0, io.EOF 91 } 92 93 // Meta returns the encrypted stream metadata for use in decrypting. This should only be called after the stream is finished 94 func (s *StreamEncrypter) Meta() StreamMeta { 95 return StreamMeta{IV: s.IV, Hash: s.Mac.Sum(nil)} 96 } 97 98 // Read reads bytes from the underlying reader and then decrypts them 99 func (s *StreamDecrypter) Read(p []byte) (int, error) { 100 n, readErr := s.Source.Read(p) 101 if n > 0 { 102 err := writeHash(s.Mac, p[:n]) 103 if err != nil { 104 return n, ex.New(err) 105 } 106 s.Stream.XORKeyStream(p[:n], p[:n]) 107 return n, readErr 108 } 109 return 0, io.EOF 110 } 111 112 // Authenticate verifys that the hash of the stream is correct. This should only be called after processing is finished 113 func (s *StreamDecrypter) Authenticate() error { 114 if !hmac.Equal(s.Meta.Hash, s.Mac.Sum(nil)) { 115 return ex.New("authentication failed") 116 } 117 return nil 118 } 119 120 func writeHash(mac hash.Hash, p []byte) error { 121 m, err := mac.Write(p) 122 if err != nil { 123 return ex.New(err) 124 } 125 if m != len(p) { 126 return ex.New("could not write all bytes to hmac") 127 } 128 return nil 129 } 130 131 func checkedWrite(dst io.Writer, p []byte) (int, error) { 132 n, err := dst.Write(p) 133 if err != nil { 134 return n, ex.New(err) 135 } 136 if n != len(p) { 137 return n, ex.New("unable to write all bytes") 138 } 139 return len(p), nil 140 } 141 142 // StreamMeta is metadata about an encrypted stream 143 type StreamMeta struct { 144 // IV is the initial value for the crypto function 145 IV []byte 146 // Hash is the sha256 hmac of the stream 147 Hash []byte 148 }