mirror of
https://github.com/slackhq/nebula.git
synced 2026-08-15 06:17:03 +02:00
extract the internal FIPS GCM implementation
We do this because the TLS wrapper is not thread safe on Open. instead of locking around it we can grab the internal implementation that is thread safe. This is the FIPS module implementation: `crypto/internal/fips140/aes/gcm.GCMWithXORCounterNonce` - https://github.com/golang/go/blob/go1.26.4/src/crypto/internal/fips140/aes/gcm/gcm_nonces.go#L212-L287 The wrapper is struct `crypto/tls.xorNonceAEAD`, with field `aead`: - https://github.com/golang/go/blob/go1.26.4/src/crypto/tls/cipher_suites.go#L482-L487 This can be cleaned up once these FIPS implementations are exposed directly: - https://github.com/golang/go/issues/73110
This commit is contained in:
+32
-8
@@ -4,8 +4,11 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"crypto/cipher"
|
"crypto/cipher"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
|
"reflect"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
// unsafe needed for go:linkname
|
// unsafe needed for go:linkname
|
||||||
|
_ "crypto/tls"
|
||||||
_ "unsafe"
|
_ "unsafe"
|
||||||
|
|
||||||
"github.com/flynn/noise"
|
"github.com/flynn/noise"
|
||||||
@@ -33,18 +36,18 @@ func (c cipherFn) CipherName() string { return c.name }
|
|||||||
var CipherAESGCMFIPS140 noise.CipherFunc = cipherFn{cipherAESGCMFIPS140, "AESGCM"}
|
var CipherAESGCMFIPS140 noise.CipherFunc = cipherFn{cipherAESGCMFIPS140, "AESGCM"}
|
||||||
|
|
||||||
// tls.aeadAESGCMTLS13 uses a 4 byte static prefix and an 8 byte XOR mask
|
// tls.aeadAESGCMTLS13 uses a 4 byte static prefix and an 8 byte XOR mask
|
||||||
var emptyPrefix = []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
|
var emptyNonce = []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
|
||||||
var emptyNonce = []byte{0, 0, 0, 0, 0, 0, 0, 0}
|
|
||||||
|
|
||||||
func cipherAESGCMFIPS140(k [32]byte) noise.Cipher {
|
func cipherAESGCMFIPS140(k [32]byte) noise.Cipher {
|
||||||
gcm := aeadAESGCMTLS13(k[:], emptyPrefix)
|
gcm := aeadAESGCMTLS13(k[:], emptyNonce)
|
||||||
|
gcm = extractFIPSAEAD(gcm)
|
||||||
return &aeadGCMFIPS140Cipher{
|
return &aeadGCMFIPS140Cipher{
|
||||||
AEAD: gcm,
|
AEAD: gcm,
|
||||||
ready: false,
|
ready: false,
|
||||||
nonce: func(n uint64) []byte {
|
nonce: func(n uint64) []byte {
|
||||||
// tls.aeadAESGCMTLS13 uses a 4 byte static prefix and an 8 byte nonce
|
// tls.aeadAESGCMTLS13 uses a 4 byte static prefix and an 8 byte nonce
|
||||||
var nonce [8]byte
|
var nonce [12]byte
|
||||||
binary.BigEndian.PutUint64(nonce[:], n)
|
binary.BigEndian.PutUint64(nonce[4:], n)
|
||||||
return nonce[:]
|
return nonce[:]
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -56,8 +59,29 @@ type aeadGCMFIPS140Cipher struct {
|
|||||||
nonce func(uint64) []byte
|
nonce func(uint64) []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Extract the internal FIPS GCM implementation from the tls wrapper. The TLS
|
||||||
|
// wrapper is not thread safe around Open, so instead of locking around it we
|
||||||
|
// can grab the internal implementation that is thread safe. This is the FIPS
|
||||||
|
// module implementation: `crypto/internal/fips140/aes/gcm.GCMWithXORCounterNonce`
|
||||||
|
//
|
||||||
|
// - https://github.com/golang/go/blob/go1.26.4/src/crypto/internal/fips140/aes/gcm/gcm_nonces.go#L212-L287
|
||||||
|
//
|
||||||
|
// The wrapper is struct `crypto/tls.xorNonceAEAD` , with field `aead`:
|
||||||
|
//
|
||||||
|
// - https://github.com/golang/go/blob/go1.26.4/src/crypto/tls/cipher_suites.go#L482-L487
|
||||||
|
//
|
||||||
|
// This can be cleaned up once these FIPS implementations are exposed directly:
|
||||||
|
//
|
||||||
|
// - https://github.com/golang/go/issues/73110
|
||||||
|
func extractFIPSAEAD(xorNonceAEAD cipher.AEAD) cipher.AEAD {
|
||||||
|
r := reflect.ValueOf(xorNonceAEAD)
|
||||||
|
v := r.Elem().FieldByName("aead")
|
||||||
|
v2 := reflect.NewAt(v.Type(), unsafe.Pointer(v.UnsafeAddr())).Elem()
|
||||||
|
return v2.Interface().(cipher.AEAD)
|
||||||
|
}
|
||||||
|
|
||||||
func (c *aeadGCMFIPS140Cipher) init(nonce []byte) {
|
func (c *aeadGCMFIPS140Cipher) init(nonce []byte) {
|
||||||
// crypto/tls.aeadAESGCMTLS13 expects that the first call to Seal
|
// GCMWithXORCounterNonce expects that the first call to Seal
|
||||||
// is with a counter of `0`, this is how it extracts the nonce mask.
|
// is with a counter of `0`, this is how it extracts the nonce mask.
|
||||||
// We can clean this up in the future when NewGCMWithCounterNonce or
|
// We can clean this up in the future when NewGCMWithCounterNonce or
|
||||||
// NewGCMForQUIC are available:
|
// NewGCMForQUIC are available:
|
||||||
@@ -84,11 +108,11 @@ func (c *aeadGCMFIPS140Cipher) Decrypt(out []byte, n uint64, ad, ciphertext []by
|
|||||||
|
|
||||||
func (c *aeadGCMFIPS140Cipher) EncryptDanger(out, ad, plaintext []byte, n uint64, nb []byte) ([]byte, error) {
|
func (c *aeadGCMFIPS140Cipher) EncryptDanger(out, ad, plaintext []byte, n uint64, nb []byte) ([]byte, error) {
|
||||||
binary.BigEndian.PutUint64(nb[4:], n)
|
binary.BigEndian.PutUint64(nb[4:], n)
|
||||||
out = c.Seal(out, nb[4:], plaintext, ad)
|
out = c.Seal(out, nb, plaintext, ad)
|
||||||
return out, nil
|
return out, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *aeadGCMFIPS140Cipher) DecryptDanger(out, ad, ciphertext []byte, n uint64, nb []byte) ([]byte, error) {
|
func (c *aeadGCMFIPS140Cipher) DecryptDanger(out, ad, ciphertext []byte, n uint64, nb []byte) ([]byte, error) {
|
||||||
binary.BigEndian.PutUint64(nb[4:], n)
|
binary.BigEndian.PutUint64(nb[4:], n)
|
||||||
return c.Open(out, nb[4:], ciphertext, ad)
|
return c.Open(out, nb, ciphertext, ad)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ func TestNewAESGCM(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
key, _ := hex.DecodeString("feffe9928665731c6d6a8f9467308308feffe9928665731c6d6a8f9467308308")
|
key, _ := hex.DecodeString("feffe9928665731c6d6a8f9467308308feffe9928665731c6d6a8f9467308308")
|
||||||
iv, _ := hex.DecodeString("facedbaddecaf888")
|
iv, _ := hex.DecodeString("00000000facedbaddecaf888")
|
||||||
plaintext, _ := hex.DecodeString("d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39")
|
plaintext, _ := hex.DecodeString("d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39")
|
||||||
aad, _ := hex.DecodeString("feedfacedeadbeeffeedfacedeadbeefabaddad2")
|
aad, _ := hex.DecodeString("feedfacedeadbeeffeedfacedeadbeefabaddad2")
|
||||||
expected, _ := hex.DecodeString("6a65c2edd45bd63c7e29f40e3d2ed8ba2b99f4c83135383d5676652f255059ceb24863ff10afb1089db701245da87fb88d3acd5f9dd0770cac220c3c04145caf25e190aeb775e7080401c628")
|
expected, _ := hex.DecodeString("6a65c2edd45bd63c7e29f40e3d2ed8ba2b99f4c83135383d5676652f255059ceb24863ff10afb1089db701245da87fb88d3acd5f9dd0770cac220c3c04145caf25e190aeb775e7080401c628")
|
||||||
|
|||||||
Reference in New Issue
Block a user