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