golang / go

The Go programming language
https://go.dev
BSD 3-Clause "New" or "Revised" License
123.73k stars 17.62k forks source link

proposal: crypto/cipher: add NewGCMWithRandomNonce #69981

Open FiloSottile opened 4 days ago

FiloSottile commented 4 days ago

NIST SP 800-38D and in particular FIPS 140-3 IG C.H essentially require that AES-GCM nonces be either generated according to an industry protocol (TLS, SSH) or internally at random by the cryptographic module.

This makes somewhat sense, because AES-GCM nonce reuse is catastrophic and common enough. It makes somewhat less sense because AES-GCM nonces are 96 bits, making the birthday bound of messages that can be encrypted with random nonces with low enough (2⁻³²) chance of collisions a mere 2³² (~4.3 billions).

Anyway, we should offer applications a way to comply with the requirement, and wire it up to the appropriate #69536 API.

The simplest proposal I can think of is to copy NewGCM, folding the NonceSize into the Overhead, like #54364.

// NewGCMWithRandomNonce returns the given cipher wrapped in Galois Counter
// Mode, with randomly-generated nonces. The cipher must have been created by
// [aes.NewCipher].
//
// The 96-bit nonce is prepended to the ciphertext by Seal, and is expected to
// be prepended to the plaintext by Open. The NonceSize of the AEAD is zero,
// while the Overhead is 28 bytes (the combination of nonce size and tag size).
//
// A given key MUST NOT be used to encrypt more than 2^32 messages, to limit the
// risk of a random nonce collision to negligible levels.
func NewGCMWithRandomNonce(cipher Block) (AEAD, error)

Do we want to take the opportunity to make the API return a concrete type? #54364 doesn't and to me it feels like it's not worth breaking the pattern yet (or having it not sort next to the other NewGCMs).

This will have the same implementation challenge described in https://github.com/golang/go/issues/54364#issuecomment-1725379404: since plaintext and ciphertext don't overlap perfectly when the buffer is reused, Seal will need to avoid stepping over itself.

I regret we are growing a bit of a zoo of AEADs with different properties, but I can't see a way around it (and it's in good part due to the fact that we have only had a bunch of imperfect options for decades, each sub-optimal in different ways): we'll have NewGCMSIV, NewGCMWithRandomNonce, and maybe XAES and XChaCha20Poly1305 with automatic nonces, of which NewGCMWithRandomNonce and NewGCMSIV have a low birthday bound, but NewGCMSIV is collision resistant (as in, it degrades to safe deterministic encryption); and then NewGCM (and the WithNonceSize and WithTagSize variants), NewGCMSIVWithNonce, chacha20poly1305.New, and chacha20poly1305.NewX with manual nonces, of which only NewGCMSIVWithNonce is nonce-misuse resistance. We should eventually clean this all up with a v2 API that only provides the safe ones with automatic nonces, probably with a different interface, but not in Go 1.24.

gabyhelp commented 4 days ago

Related Issues and Documentation

(Emoji vote if this was helpful or unhelpful; more detailed feedback welcome in this discussion.)

aclements commented 3 days ago

This proposal has been added to the active column of the proposals project and will now be reviewed at the weekly proposal review meetings.