Skip to content

Commit

Permalink
crypto/internal/fips140/aes: mark AES-ECB as not approved
Browse files Browse the repository at this point in the history
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 golang#69536

Change-Id: I972a4b5da8efd0a0ab68d7dd509bec73aa2d6b68
Reviewed-on: https://go-review.googlesource.com/c/go/+/636775
Reviewed-by: David Chase <[email protected]>
Reviewed-by: Roland Shoemaker <[email protected]>
Auto-Submit: Filippo Valsorda <[email protected]>
LUCI-TryBot-Result: Go LUCI <[email protected]>
  • Loading branch information
FiloSottile authored and gopherbot committed Dec 17, 2024
1 parent 427a240 commit dd7a7ba
Show file tree
Hide file tree
Showing 6 changed files with 24 additions and 15 deletions.
13 changes: 11 additions & 2 deletions src/crypto/internal/fips140/aes/aes.go
Original file line number Diff line number Diff line change
Expand Up @@ -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")
}
Expand All @@ -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")
}
Expand All @@ -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)
}
4 changes: 2 additions & 2 deletions src/crypto/internal/fips140/aes/cbc.go
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down Expand Up @@ -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])
Expand Down
2 changes: 1 addition & 1 deletion src/crypto/internal/fips140/aes/ctr.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
8 changes: 4 additions & 4 deletions src/crypto/internal/fips140/aes/gcm/cmac.go
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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 {
Expand All @@ -54,15 +54,15 @@ 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 {
// Final incomplete block.
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
}
Expand Down
4 changes: 2 additions & 2 deletions src/crypto/internal/fips140/aes/gcm/gcm_asm.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down
8 changes: 4 additions & 4 deletions src/crypto/internal/fips140/aes/gcm/gcm_generic.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand All @@ -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)

Expand Down Expand Up @@ -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[:])
Expand All @@ -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[:])
}
Expand Down

0 comments on commit dd7a7ba

Please sign in to comment.