crypto/internal/fips140/aes: mark AES-ECB as not approved

NIST SP 800-131Ar3 ipd, scheduled for publication in 2025Q1, marks
AES-ECB as disallowed for encryption, and legacy use for decryption.

There are apparently no details on how the transition is going to work,
so to avoid surprises we just mark direct use of the Block as
non-approved.

We need to use Encrypt from higher level modes without tripping the
service indicator. Within the aes package, we just use the internal
function. For the gcm package we could do something more clever, but
this deep into the freeze, just make an exported function that we commit
to use nowhere else.

I could not figure out a decent way to block ECB on GODEBUG=fips140=only.

For #69536

Change-Id: I972a4b5da8efd0a0ab68d7dd509bec73aa2d6b68
Reviewed-on: https://go-review.googlesource.com/c/go/+/636775
Reviewed-by: David Chase <drchase@google.com>
Reviewed-by: Roland Shoemaker <roland@golang.org>
Auto-Submit: Filippo Valsorda <filippo@golang.org>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
This commit is contained in:
Filippo Valsorda 2024-12-16 19:24:20 +01:00 committed by Gopher Robot
parent 427a2401af
commit dd7a7ba38f
6 changed files with 24 additions and 15 deletions

View file

@ -94,6 +94,8 @@ func newBlockExpanded(c *blockExpanded, key []byte) {
func (c *Block) BlockSize() int { return BlockSize }
func (c *Block) Encrypt(dst, src []byte) {
// AES-ECB is not approved in FIPS 140-3 mode.
fips140.RecordNonApproved()
if len(src) < BlockSize {
panic("crypto/aes: input not full block")
}
@ -103,11 +105,12 @@ func (c *Block) Encrypt(dst, src []byte) {
if alias.InexactOverlap(dst[:BlockSize], src[:BlockSize]) {
panic("crypto/aes: invalid buffer overlap")
}
fips140.RecordApproved()
encryptBlock(c, dst, src)
}
func (c *Block) Decrypt(dst, src []byte) {
// AES-ECB is not approved in FIPS 140-3 mode.
fips140.RecordNonApproved()
if len(src) < BlockSize {
panic("crypto/aes: input not full block")
}
@ -117,6 +120,12 @@ func (c *Block) Decrypt(dst, src []byte) {
if alias.InexactOverlap(dst[:BlockSize], src[:BlockSize]) {
panic("crypto/aes: invalid buffer overlap")
}
fips140.RecordApproved()
decryptBlock(c, dst, src)
}
// EncryptBlockInternal applies the AES encryption function to one block.
//
// It is an internal function meant only for the gcm package.
func EncryptBlockInternal(c *Block, dst, src []byte) {
encryptBlock(c, dst, src)
}

View file

@ -50,7 +50,7 @@ func cryptBlocksEncGeneric(b *Block, civ *[BlockSize]byte, dst, src []byte) {
for len(src) > 0 {
// Write the xor to dst, then encrypt in place.
subtle.XORBytes(dst[:BlockSize], src[:BlockSize], iv)
b.Encrypt(dst[:BlockSize], dst[:BlockSize])
encryptBlock(b, dst[:BlockSize], dst[:BlockSize])
// Move to the next block with this block as the next iv.
iv = dst[:BlockSize]
@ -111,7 +111,7 @@ func cryptBlocksDecGeneric(b *Block, civ *[BlockSize]byte, dst, src []byte) {
copy(civ[:], src[start:end])
for start >= 0 {
b.Decrypt(dst[start:end], src[start:end])
decryptBlock(b, dst[start:end], src[start:end])
if start > 0 {
subtle.XORBytes(dst[start:end], dst[start:end], src[prev:start])

View file

@ -132,7 +132,7 @@ func ctrBlocks(b *Block, dst, src []byte, ivlo, ivhi uint64) {
byteorder.BEPutUint64(buf[i:], ivhi)
byteorder.BEPutUint64(buf[i+8:], ivlo)
ivlo, ivhi = add128(ivlo, ivhi, 1)
b.Encrypt(buf[i:], buf[i:])
encryptBlock(b, buf[i:], buf[i:])
}
// XOR into buf first, in case src and dst overlap (see above).
subtle.XORBytes(buf, src, buf)

View file

@ -28,7 +28,7 @@ func NewCMAC(b *aes.Block) *CMAC {
}
func (c *CMAC) deriveSubkeys() {
c.b.Encrypt(c.k1[:], c.k1[:])
aes.EncryptBlockInternal(&c.b, c.k1[:], c.k1[:])
msb := shiftLeft(&c.k1)
c.k1[len(c.k1)-1] ^= msb * 0b10000111
@ -45,7 +45,7 @@ func (c *CMAC) MAC(m []byte) [aes.BlockSize]byte {
// Special-cased as a single empty partial final block.
x = c.k2
x[len(m)] ^= 0b10000000
c.b.Encrypt(x[:], x[:])
aes.EncryptBlockInternal(&c.b, x[:], x[:])
return x
}
for len(m) >= aes.BlockSize {
@ -54,7 +54,7 @@ func (c *CMAC) MAC(m []byte) [aes.BlockSize]byte {
// Final complete block.
subtle.XORBytes(x[:], c.k1[:], x[:])
}
c.b.Encrypt(x[:], x[:])
aes.EncryptBlockInternal(&c.b, x[:], x[:])
m = m[aes.BlockSize:]
}
if len(m) > 0 {
@ -62,7 +62,7 @@ func (c *CMAC) MAC(m []byte) [aes.BlockSize]byte {
subtle.XORBytes(x[:], m, x[:])
subtle.XORBytes(x[:], c.k2[:], x[:])
x[len(m)] ^= 0b10000000
c.b.Encrypt(x[:], x[:])
aes.EncryptBlockInternal(&c.b, x[:], x[:])
}
return x
}

View file

@ -81,7 +81,7 @@ func seal(out []byte, g *GCM, nonce, plaintext, data []byte) {
gcmAesFinish(&g.productTable, &tagMask, &counter, uint64(len(nonce)), uint64(0))
}
g.cipher.Encrypt(tagMask[:], counter[:])
aes.EncryptBlockInternal(&g.cipher, tagMask[:], counter[:])
var tagOut [gcmTagSize]byte
gcmAesData(&g.productTable, data, &tagOut)
@ -114,7 +114,7 @@ func open(out []byte, g *GCM, nonce, ciphertext, data []byte) error {
gcmAesFinish(&g.productTable, &tagMask, &counter, uint64(len(nonce)), uint64(0))
}
g.cipher.Encrypt(tagMask[:], counter[:])
aes.EncryptBlockInternal(&g.cipher, tagMask[:], counter[:])
var expectedTag [gcmTagSize]byte
gcmAesData(&g.productTable, data, &expectedTag)

View file

@ -12,7 +12,7 @@ import (
func sealGeneric(out []byte, g *GCM, nonce, plaintext, additionalData []byte) {
var H, counter, tagMask [gcmBlockSize]byte
g.cipher.Encrypt(H[:], H[:])
aes.EncryptBlockInternal(&g.cipher, H[:], H[:])
deriveCounterGeneric(&H, &counter, nonce)
gcmCounterCryptGeneric(&g.cipher, tagMask[:], tagMask[:], &counter)
@ -25,7 +25,7 @@ func sealGeneric(out []byte, g *GCM, nonce, plaintext, additionalData []byte) {
func openGeneric(out []byte, g *GCM, nonce, ciphertext, additionalData []byte) error {
var H, counter, tagMask [gcmBlockSize]byte
g.cipher.Encrypt(H[:], H[:])
aes.EncryptBlockInternal(&g.cipher, H[:], H[:])
deriveCounterGeneric(&H, &counter, nonce)
gcmCounterCryptGeneric(&g.cipher, tagMask[:], tagMask[:], &counter)
@ -70,7 +70,7 @@ func gcmCounterCryptGeneric(b *aes.Block, out, src []byte, counter *[gcmBlockSiz
var mask [gcmBlockSize]byte
for len(src) >= gcmBlockSize {
b.Encrypt(mask[:], counter[:])
aes.EncryptBlockInternal(b, mask[:], counter[:])
gcmInc32(counter)
subtle.XORBytes(out, src, mask[:])
@ -79,7 +79,7 @@ func gcmCounterCryptGeneric(b *aes.Block, out, src []byte, counter *[gcmBlockSiz
}
if len(src) > 0 {
b.Encrypt(mask[:], counter[:])
aes.EncryptBlockInternal(b, mask[:], counter[:])
gcmInc32(counter)
subtle.XORBytes(out, src, mask[:])
}