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