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