// Copyright 2024 The Go Authors. All rights reserved.// Use of this source code is governed by a BSD-style// license that can be found in the LICENSE file.package cipherimport ()const ( gcmBlockSize = 16 gcmStandardNonceSize = 12 gcmTagSize = 16 gcmMinimumTagSize = 12// NIST SP 800-38D recommends tags with 12 or more bytes.)// NewGCM returns the given 128-bit, block cipher wrapped in Galois Counter Mode// with the standard nonce length.//// In general, the GHASH operation performed by this implementation of GCM is not constant-time.// An exception is when the underlying [Block] was created by aes.NewCipher// on systems with hardware support for AES. See the [crypto/aes] package documentation for details.func ( Block) (AEAD, error) {iffips140only.Enabled {returnnil, errors.New("crypto/cipher: use of GCM with arbitrary IVs is not allowed in FIPS 140-only mode, use NewGCMWithRandomNonce") }returnnewGCM(, gcmStandardNonceSize, gcmTagSize)}// NewGCMWithNonceSize returns the given 128-bit, block cipher wrapped in Galois// Counter Mode, which accepts nonces of the given length. The length must not// be zero.//// Only use this function if you require compatibility with an existing// cryptosystem that uses non-standard nonce lengths. All other users should use// [NewGCM], which is faster and more resistant to misuse.func ( Block, int) (AEAD, error) {iffips140only.Enabled {returnnil, errors.New("crypto/cipher: use of GCM with arbitrary IVs is not allowed in FIPS 140-only mode, use NewGCMWithRandomNonce") }returnnewGCM(, , gcmTagSize)}// NewGCMWithTagSize returns the given 128-bit, block cipher wrapped in Galois// Counter Mode, which generates tags with the given length.//// Tag sizes between 12 and 16 bytes are allowed.//// Only use this function if you require compatibility with an existing// cryptosystem that uses non-standard tag lengths. All other users should use// [NewGCM], which is more resistant to misuse.func ( Block, int) (AEAD, error) {iffips140only.Enabled {returnnil, errors.New("crypto/cipher: use of GCM with arbitrary IVs is not allowed in FIPS 140-only mode, use NewGCMWithRandomNonce") }returnnewGCM(, gcmStandardNonceSize, )}func newGCM( Block, , int) (AEAD, error) { , := .(*aes.Block)if ! {iffips140only.Enabled {returnnil, errors.New("crypto/cipher: use of GCM with non-AES ciphers is not allowed in FIPS 140-only mode") }returnnewGCMFallback(, , ) }// We don't return gcm.New directly, because it would always return a non-nil // AEAD interface value with type *gcm.GCM even if the *gcm.GCM is nil. , := gcm.New(, , )if != nil {returnnil, }return , nil}// NewGCMWithRandomNonce returns the given cipher wrapped in Galois Counter// Mode, with randomly-generated nonces. The cipher must have been created by// [aes.NewCipher].//// It generates a random 96-bit nonce, which is prepended to the ciphertext by Seal,// and is extracted from the ciphertext 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 ( Block) (AEAD, error) { , := .(*aes.Block)if ! {returnnil, errors.New("cipher: NewGCMWithRandomNonce requires aes.Block") } , := gcm.New(, gcmStandardNonceSize, gcmTagSize)if != nil {returnnil, }returngcmWithRandomNonce{}, nil}type gcmWithRandomNonce struct { *gcm.GCM}func ( gcmWithRandomNonce) () int {return0}func ( gcmWithRandomNonce) () int {returngcmStandardNonceSize + gcmTagSize}func ( gcmWithRandomNonce) (, , , []byte) []byte {iflen() != 0 {panic("crypto/cipher: non-empty nonce passed to GCMWithRandomNonce") } , := sliceForAppend(, gcmStandardNonceSize+len()+gcmTagSize)ifalias.InexactOverlap(, ) {panic("crypto/cipher: invalid buffer overlap of output and input") }ifalias.AnyOverlap(, ) {panic("crypto/cipher: invalid buffer overlap of output and additional data") } = [:gcmStandardNonceSize] := [gcmStandardNonceSize:]// The AEAD interface allows using plaintext[:0] or ciphertext[:0] as dst. // // This is kind of a problem when trying to prepend or trim a nonce, because the // actual AES-GCTR blocks end up overlapping but not exactly. // // In Open, we write the output *before* the input, so unless we do something // weird like working through a chunk of block backwards, it works out. // // In Seal, we could work through the input backwards or intentionally load // ahead before writing. // // However, the crypto/internal/fips140/aes/gcm APIs also check for exact overlap, // so for now we just do a memmove if we detect overlap. // // ┌───────────────────────────┬ ─ ─ // │PPPPPPPPPPPPPPPPPPPPPPPPPPP│ │ // └▽─────────────────────────▲┴ ─ ─ // ╲ Seal ╲ // ╲ Open ╲ // ┌───▼─────────────────────────△──┐ // │NN|CCCCCCCCCCCCCCCCCCCCCCCCCCC|T│ // └────────────────────────────────┘ //ifalias.AnyOverlap(, ) {copy(, ) = [:len()] }gcm.SealWithRandomNonce(.GCM, , , , )return}func ( gcmWithRandomNonce) (, , , []byte) ([]byte, error) {iflen() != 0 {panic("crypto/cipher: non-empty nonce passed to GCMWithRandomNonce") }iflen() < gcmStandardNonceSize+gcmTagSize {returnnil, errOpen } , := sliceForAppend(, len()-gcmStandardNonceSize-gcmTagSize)ifalias.InexactOverlap(, ) {panic("crypto/cipher: invalid buffer overlap of output and input") }ifalias.AnyOverlap(, ) {panic("crypto/cipher: invalid buffer overlap of output and additional data") }// See the discussion in Seal. Note that if there is any overlap at this // point, it's because out = ciphertext, so out must have enough capacity // even if we sliced the tag off. Also note how [AEAD] specifies that "the // contents of dst, up to its capacity, may be overwritten".ifalias.AnyOverlap(, ) { = make([]byte, gcmStandardNonceSize)copy(, )copy([:len()], [gcmStandardNonceSize:]) = [:len()-gcmStandardNonceSize] } else { = [:gcmStandardNonceSize] = [gcmStandardNonceSize:] } , := .GCM.Open([:0], , , )if != nil {returnnil, }return , nil}// gcmAble is an interface implemented by ciphers that have a specific optimized// implementation of GCM. crypto/aes doesn't use this anymore, and we'd like to// eventually remove it.type gcmAble interface { NewGCM(nonceSize, tagSize int) (AEAD, error)}func newGCMFallback( Block, , int) (AEAD, error) {if < gcmMinimumTagSize || > gcmBlockSize {returnnil, errors.New("cipher: incorrect tag size given to GCM") }if <= 0 {returnnil, errors.New("cipher: the nonce can't have zero length") }if , := .(gcmAble); {return .NewGCM(, ) }if .BlockSize() != gcmBlockSize {returnnil, errors.New("cipher: NewGCM requires 128-bit block cipher") }return &gcmFallback{cipher: , nonceSize: , tagSize: }, nil}// gcmFallback is only used for non-AES ciphers, which regrettably we// theoretically support. It's a copy of the generic implementation from// crypto/internal/fips140/aes/gcm/gcm_generic.go, refer to that file for more details.type gcmFallback struct { cipher Block nonceSize int tagSize int}func ( *gcmFallback) () int {return .nonceSize}func ( *gcmFallback) () int {return .tagSize}func ( *gcmFallback) (, , , []byte) []byte {iflen() != .nonceSize {panic("crypto/cipher: incorrect nonce length given to GCM") }if .nonceSize == 0 {panic("crypto/cipher: incorrect GCM nonce size") }ifuint64(len()) > uint64((1<<32)-2)*gcmBlockSize {panic("crypto/cipher: message too large for GCM") } , := sliceForAppend(, len()+.tagSize)ifalias.InexactOverlap(, ) {panic("crypto/cipher: invalid buffer overlap of output and input") }ifalias.AnyOverlap(, ) {panic("crypto/cipher: invalid buffer overlap of output and additional data") }var , , [gcmBlockSize]byte .cipher.Encrypt([:], [:])deriveCounter(&, &, )gcmCounterCryptGeneric(.cipher, [:], [:], &)gcmCounterCryptGeneric(.cipher, , , &)var [gcmTagSize]bytegcmAuth([:], &, &, [:len()], )copy([len():], [:])return}var errOpen = errors.New("cipher: message authentication failed")func ( *gcmFallback) (, , , []byte) ([]byte, error) {iflen() != .nonceSize {panic("crypto/cipher: incorrect nonce length given to GCM") }if .tagSize < gcmMinimumTagSize {panic("crypto/cipher: incorrect GCM tag size") }iflen() < .tagSize {returnnil, errOpen }ifuint64(len()) > uint64((1<<32)-2)*gcmBlockSize+uint64(.tagSize) {returnnil, errOpen } , := sliceForAppend(, len()-.tagSize)ifalias.InexactOverlap(, ) {panic("crypto/cipher: invalid buffer overlap of output and input") }ifalias.AnyOverlap(, ) {panic("crypto/cipher: invalid buffer overlap of output and additional data") }var , , [gcmBlockSize]byte .cipher.Encrypt([:], [:])deriveCounter(&, &, )gcmCounterCryptGeneric(.cipher, [:], [:], &) := [len()-.tagSize:] = [:len()-.tagSize]var [gcmTagSize]bytegcmAuth([:], &, &, , )ifsubtle.ConstantTimeCompare([:.tagSize], ) != 1 {// We sometimes decrypt and authenticate concurrently, so we overwrite // dst in the event of a tag mismatch. To be consistent across platforms // and to avoid releasing unauthenticated plaintext, we clear the buffer // in the event of an error.clear()returnnil, errOpen }gcmCounterCryptGeneric(.cipher, , , &)return , nil}func deriveCounter(, *[gcmBlockSize]byte, []byte) {iflen() == gcmStandardNonceSize {copy([:], ) [gcmBlockSize-1] = 1 } else { := make([]byte, 16)byteorder.BEPutUint64([8:], uint64(len())*8) := gcm.GHASH(, , )copy([:], ) }}func gcmCounterCryptGeneric( Block, , []byte, *[gcmBlockSize]byte) {var [gcmBlockSize]byteforlen() >= gcmBlockSize { .Encrypt([:], [:])gcmInc32()subtle.XORBytes(, , [:]) = [gcmBlockSize:] = [gcmBlockSize:] }iflen() > 0 { .Encrypt([:], [:])gcmInc32()subtle.XORBytes(, , [:]) }}func gcmInc32( *[gcmBlockSize]byte) { := [len()-4:]byteorder.BEPutUint32(, byteorder.BEUint32()+1)}func gcmAuth( []byte, , *[gcmBlockSize]byte, , []byte) { := make([]byte, 16)byteorder.BEPutUint64([:8], uint64(len())*8)byteorder.BEPutUint64([8:], uint64(len())*8) := gcm.GHASH(, , , )subtle.XORBytes(, , [:])}// sliceForAppend takes a slice and a requested number of bytes. It returns a// slice with the contents of the given slice followed by that many bytes and a// second slice that aliases into it and contains only the extra bytes. If the// original slice has sufficient capacity then no allocation is performed.func sliceForAppend( []byte, int) (, []byte) {if := len() + ; cap() >= { = [:] } else { = make([]byte, )copy(, ) } = [len():]return}
The pages are generated with Goldsv0.7.3. (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu.
PR and bug reports are welcome and can be submitted to the issue list.
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds.