174 lines
5.7 KiB
Go
174 lines
5.7 KiB
Go
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[:]
|
||
}
|