1package miscreant 2 3import ( 4 "crypto/cipher" 5 "encoding/binary" 6) 7 8// streamNoncePrefixSize is the user-supplied nonce size 9const streamNoncePrefixSize = 8 10 11// streamExtendedNonceSize is the nonce prefix + 32-bit counter + 1-byte last block flag 12const streamExtendedNonceSize = streamNoncePrefixSize + 4 + 1 13 14// lastBlockFlag indicates that a block is the last in the STREAM 15const lastBlockFlag byte = 1 16 17// counterMax is the maximum allowable value for the stream counter 18const counterMax uint64 = 0xFFFFFFFF 19 20// StreamEncryptor encrypts message streams, selecting the nonces using a 21// 32-bit counter, generalized for any cipher.AEAD algorithm 22// 23// This construction corresponds to the ℰ stream encryptor object as defined in 24// the paper Online Authenticated-Encryption and its Nonce-Reuse Misuse-Resistance 25type StreamEncryptor struct { 26 // cipher.AEAD instance underlying this STREAM 27 a cipher.AEAD 28 29 // Nonce encoder instance which computes per-message nonces 30 n *nonceEncoder32 31} 32 33// NewStreamEncryptor returns a STREAM encryptor instance with the given 34// cipher, nonce, and a key which must be twice as long as an AES key, either 35// 32 or 64 bytes to select AES-128 (AES-SIV-256) or AES-256 (AES-SIV-512). 36func NewStreamEncryptor(alg string, key, nonce []byte) (*StreamEncryptor, error) { 37 aead, err := NewAEAD(alg, key, streamExtendedNonceSize) 38 if err != nil { 39 return nil, err 40 } 41 42 nonceEncoder, err := newNonceEncoder32(nonce) 43 if err != nil { 44 return nil, err 45 } 46 47 return &StreamEncryptor{a: aead, n: nonceEncoder}, nil 48} 49 50// NonceSize returns the size of the nonce that must be passed to 51// NewStreamEncryptor 52func (e *StreamEncryptor) NonceSize() int { return streamNoncePrefixSize } 53 54// Overhead returns the maximum difference between the lengths of a 55// plaintext and its ciphertext, which in the case of AES-SIV modes 56// is the size of the initialization vector 57func (e *StreamEncryptor) Overhead() int { return e.a.Overhead() } 58 59// Seal the next message in the STREAM, which encrypts and authenticates 60// plaintext, authenticates the additional data and appends the result to dst, 61// returning the updated slice. 62// 63// The plaintext and dst may alias exactly or not at all. To reuse 64// plaintext's storage for the encrypted output, use plaintext[:0] as dst. 65// 66// The lastBlock argument should be set to true if this is the last message 67// in the STREAM. No further messages can be encrypted after the last one 68func (e *StreamEncryptor) Seal(dst, plaintext, aData []byte, lastBlock bool) []byte { 69 return e.a.Seal(dst, e.n.Next(lastBlock), plaintext, aData) 70} 71 72// StreamDecryptor decrypts message streams, selecting the nonces using a 73// 32-bit counter, generalized for any cipher.AEAD algorithm 74// 75// This construction corresponds to the ℰ stream encryptor object as defined in 76// the paper Online Authenticated-Encryption and its Nonce-Reuse Misuse-Resistance 77type StreamDecryptor struct { 78 // cipher.AEAD instance underlying this STREAM 79 a cipher.AEAD 80 81 // Nonce encoder instance which computes per-message nonces 82 n *nonceEncoder32 83} 84 85// NewStreamDecryptor returns a STREAM encryptor instance with the given 86// cipher, nonce, and a key which must be twice as long as an AES key, either 87// 32 or 64 bytes to select AES-128 (AES-SIV-256) or AES-256 (AES-SIV-512). 88func NewStreamDecryptor(alg string, key, nonce []byte) (*StreamDecryptor, error) { 89 aead, err := NewAEAD(alg, key, streamExtendedNonceSize) 90 if err != nil { 91 return nil, err 92 93 } 94 95 nonceEncoder, err := newNonceEncoder32(nonce) 96 if err != nil { 97 return nil, err 98 } 99 100 return &StreamDecryptor{a: aead, n: nonceEncoder}, nil 101} 102 103// NonceSize returns the size of the nonce that must be passed to 104// NewStreamDecryptor 105func (d *StreamDecryptor) NonceSize() int { return streamNoncePrefixSize } 106 107// Overhead returns the maximum difference between the lengths of a 108// plaintext and its ciphertext, which in the case of AES-SIV modes 109// is the size of the initialization vector 110func (d *StreamDecryptor) Overhead() int { return d.a.Overhead() } 111 112// Open decrypts and authenticates the next ciphertext in the STREAM, 113// and also authenticates the additional data, ensuring it matches 114// the value passed to Seal. 115// 116// If successful, it appends the resulting plaintext to dst and returns 117// the updated slice. 118// 119// The ciphertext and dst may alias exactly or not at all. To reuse 120// ciphertext's storage for the decrypted output, use ciphertext[:0] as dst. 121// 122// Even if the function fails, the contents of dst, up to its capacity, 123// may be overwritten. 124func (d *StreamDecryptor) Open(dst, ciphertext, aData []byte, lastBlock bool) ([]byte, error) { 125 return d.a.Open(dst, d.n.Next(lastBlock), ciphertext, aData) 126} 127 128// Computes STREAM nonces based on the current position in the STREAM. 129// 130// Accepts a 64-bit nonce and uses a 32-bit counter internally. 131// 132// Panics if the nonce size is incorrect, or the 32-bit counter overflows 133type nonceEncoder32 struct { 134 value [streamExtendedNonceSize]byte 135 counter uint64 136 finished bool 137} 138 139func newNonceEncoder32(noncePrefix []byte) (*nonceEncoder32, error) { 140 if len(noncePrefix) != streamNoncePrefixSize { 141 panic("miscreant.STREAM: incorrect nonce length") 142 } 143 144 value := [streamExtendedNonceSize]byte{0} 145 copy(value[:streamNoncePrefixSize], noncePrefix) 146 147 return &nonceEncoder32{ 148 value: value, 149 counter: 0, 150 finished: false, 151 }, nil 152} 153 154func (n *nonceEncoder32) Next(lastBlock bool) []byte { 155 if n.finished { 156 panic("miscreant.STREAM: already finished") 157 } 158 159 counterSlice := n.value[streamNoncePrefixSize : streamNoncePrefixSize+4] 160 binary.BigEndian.PutUint32(counterSlice, uint32(n.counter)) 161 162 if lastBlock { 163 n.value[len(n.value)-1] = lastBlockFlag 164 n.finished = true 165 } else { 166 n.counter++ 167 if n.counter > counterMax { 168 panic("miscreant.STREAM: nonce counter overflowed") 169 } 170 } 171 172 return n.value[:] 173} 174