This commit is contained in:
Wade Simmons
2026-06-09 10:30:51 -04:00
parent 7cd3875934
commit 06fb503fc3
8 changed files with 61 additions and 35 deletions
+12 -7
View File
@@ -188,15 +188,20 @@ build/linux-arm64-boringcrypto/%: LDFLAGS += -checklinkname=0
# fips140 # fips140
build/linux-amd64-fips140/%: GOENV += GOFIPS140=v1.0.0 build/linux-amd64-fips140/%: GOENV += GOFIPS140=v1.0.0
build/linux-amd64-fips140/%: LDFLAGS += -checklinkname=0 -X runtime.godebugDefault=fips140=only build/linux-amd64-fips140/%: LDFLAGS += -X runtime.godebugDefault=fips140=only
build/linux-amd64-fips140/%: BUILD_ARGS += -tags fips140
build/linux-arm64-fips140/%: GOENV += GOFIPS140=v1.0.0 build/linux-arm64-fips140/%: GOENV += GOFIPS140=v1.0.0
build/linux-arm64-fips140/%: LDFLAGS += -checklinkname=0 -X runtime.godebugDefault=fips140=only build/linux-arm64-fips140/%: LDFLAGS += -X runtime.godebugDefault=fips140=only
build/linux-arm64-fips140/%: BUILD_ARGS += -tags fips140
build/darwin-arm64-fips140/%: GOENV += GOFIPS140=v1.0.0 build/darwin-arm64-fips140/%: GOENV += GOFIPS140=v1.0.0
build/darwin-arm64-fips140/%: LDFLAGS += -checklinkname=0 -X runtime.godebugDefault=fips140=only build/darwin-arm64-fips140/%: LDFLAGS += -X runtime.godebugDefault=fips140=only
build/darwin-arm64-fips140/%: BUILD_ARGS += -tags fips140
build/windows-amd64-fips140/%: GOENV += GOFIPS140=v1.0.0 build/windows-amd64-fips140/%: GOENV += GOFIPS140=v1.0.0
build/windows-amd64-fips140/%: LDFLAGS += -checklinkname=0 -X runtime.godebugDefault=fips140=only build/windows-amd64-fips140/%: LDFLAGS += -X runtime.godebugDefault=fips140=only
build/windows-amd64-fips140/%: BUILD_ARGS += -tags fips140
build/windows-arm64-fips140/%: GOENV += GOFIPS140=v1.0.0 build/windows-arm64-fips140/%: GOENV += GOFIPS140=v1.0.0
build/windows-arm64-fips140/%: LDFLAGS += -checklinkname=0 -X runtime.godebugDefault=fips140=only build/windows-arm64-fips140/%: LDFLAGS += -X runtime.godebugDefault=fips140=only
build/windows-arm64-fips140/%: BUILD_ARGS += -tags fips140
build/%/nebula: .FORCE build/%/nebula: .FORCE
GOOS=$(firstword $(subst -, , $*)) \ GOOS=$(firstword $(subst -, , $*)) \
@@ -280,8 +285,8 @@ endif
fips140: fips140:
@echo > $(NULL_FILE) @echo > $(NULL_FILE)
$(eval GOENV += GOFIPS140=v1.0.0) $(eval GOENV += GOFIPS140=v1.0.0)
$(eval LDFLAGS += -checklinkname=0 -X runtime.godebugDefault=fips140=only) $(eval LDFLAGS += -X runtime.godebugDefault=fips140=only)
$(eval TEST_FLAGS += -ldflags -checklinkname=0) $(eval BUILD_ARGS += -tags fips140)
$(eval TEST_ENV += $(GOENV)) $(eval TEST_ENV += $(GOENV))
ifeq ($(words $(MAKECMDGOALS)),1) ifeq ($(words $(MAKECMDGOALS)),1)
@$(MAKE) fips140 ${.DEFAULT_GOAL} --no-print-directory @$(MAKE) fips140 ${.DEFAULT_GOAL} --no-print-directory
+1 -1
View File
@@ -1,4 +1,4 @@
//go:build fips140v1.0 || fips140v1.26 //go:build fips140
package main package main
+1 -1
View File
@@ -1,4 +1,4 @@
//go:build fips140v1.0 || fips140v1.26 //go:build fips140
package main package main
+1 -1
View File
@@ -1,4 +1,4 @@
//go:build fips140v1.0 || fips140v1.26 //go:build fips140
package main package main
+1 -1
View File
@@ -33,7 +33,7 @@ func NewCipherState(s *noise.CipherState, cipherFunc noise.CipherFunc) CipherSta
return cs return cs
} }
switch cipherFunc.CipherName() { switch cipherFunc.CipherName() {
case CipherAESGCM.CipherName(): case noise.CipherAESGCM.CipherName():
return NewCipherStateAESGCM(s) return NewCipherStateAESGCM(s)
case noise.CipherChaChaPoly.CipherName(): case noise.CipherChaChaPoly.CipherName():
return NewCipherStateChaChaPoly(s) return NewCipherStateChaChaPoly(s)
+29 -21
View File
@@ -1,3 +1,5 @@
//go:build !boringcrypto
package noiseutil package noiseutil
import ( import (
@@ -11,9 +13,10 @@ import (
"github.com/flynn/noise" "github.com/flynn/noise"
) )
// TODO: Use NewGCMWithCounterNonce once available: // TODO: Use NewGCMWithCounterNonce or NewGCMForQUIC once available:
// - https://github.com/golang/go/issues/73110 // - https://github.com/golang/go/issues/73110
// Using tls.aeadAESGCM gives us the TLS 1.2 GCM, which also verifies // - https://github.com/golang/go/issues/79219
// Using tls.aeadAESGCMTLS13 gives us the TLS 1.3 GCM, which also verifies
// that the nonce is strictly increasing. // that the nonce is strictly increasing.
// //
//go:linkname aeadAESGCMTLS13 crypto/tls.aeadAESGCMTLS13 //go:linkname aeadAESGCMTLS13 crypto/tls.aeadAESGCMTLS13
@@ -28,7 +31,7 @@ func (c cipherFn) Cipher(k [32]byte) noise.Cipher { return c.fn(k) }
func (c cipherFn) CipherName() string { return c.name } func (c cipherFn) CipherName() string { return c.name }
// CipherAESGCM is the AES256-GCM AEAD cipher (using aeadAESGCM when fips140 is enabled) // CipherAESGCM is the AES256-GCM AEAD cipher (using aeadAESGCM when fips140 is enabled)
var CipherAESGCM 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 emptyPrefix = []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
@@ -36,11 +39,11 @@ 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[:], emptyPrefix)
return aeadCipher{ return &aeadCipher{
gcm, AEAD: gcm,
false, ready: false,
func(n uint64) []byte { nonce: func(n uint64) []byte {
// tls.aeadAESGCM 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 [8]byte
binary.BigEndian.PutUint64(nonce[:], n) binary.BigEndian.PutUint64(nonce[:], n)
return nonce[:] return nonce[:]
@@ -48,37 +51,42 @@ func cipherAESGCMFIPS140(k [32]byte) noise.Cipher {
} }
} }
type aeadCipher struct { func (c *aeadCipher) Seal(dst, nonce, plaintext, additionalData []byte) []byte {
cipher.AEAD // runtime.Breakpoint()
initialized bool if !c.ready {
nonce func(uint64) []byte // crypto/tls.aeadAESGCMTLS13 expected that the first call to Seal
} // 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
func (c aeadCipher) Seal(dst, nonce, plaintext, additionalData []byte) []byte { // NewGCMForQUIC are available:
if !c.initialized {
if !bytes.Equal(emptyNonce, nonce) { if !bytes.Equal(emptyNonce, nonce) {
c.AEAD.Seal([]byte{}, emptyNonce, []byte{}, []byte{}) c.AEAD.Seal([]byte{}, emptyNonce, []byte{}, []byte{})
} }
c.initialized = true c.ready = true
} }
return c.AEAD.Seal(dst, nonce, plaintext, additionalData) return c.AEAD.Seal(dst, nonce, plaintext, additionalData)
} }
func (c aeadCipher) Encrypt(out []byte, n uint64, ad, plaintext []byte) []byte { type aeadCipher struct {
cipher.AEAD
ready bool
nonce func(uint64) []byte
}
func (c *aeadCipher) Encrypt(out []byte, n uint64, ad, plaintext []byte) []byte {
return c.Seal(out, c.nonce(n), plaintext, ad) return c.Seal(out, c.nonce(n), plaintext, ad)
} }
func (c aeadCipher) Decrypt(out []byte, n uint64, ad, ciphertext []byte) ([]byte, error) { func (c *aeadCipher) Decrypt(out []byte, n uint64, ad, ciphertext []byte) ([]byte, error) {
return c.Open(out, c.nonce(n), ciphertext, ad) return c.Open(out, c.nonce(n), ciphertext, ad)
} }
func (c aeadCipher) EncryptDanger(out, ad, plaintext []byte, n uint64, nb []byte) ([]byte, error) { func (c *aeadCipher) 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[4:], plaintext, ad)
return out, nil return out, nil
} }
func (c aeadCipher) DecryptDanger(out, ad, ciphertext []byte, n uint64, nb []byte) ([]byte, error) { func (c *aeadCipher) 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[4:], ciphertext, ad)
} }
+4 -1
View File
@@ -33,7 +33,10 @@ func TestNewAESGCM(t *testing.T) {
assert.Equal(t, expected, dst) assert.Equal(t, expected, dst)
// We expect this to fail since we are re-encrypting with a repeat IV // We expect this to fail since we are re-encrypting with a repeat IV
assert.PanicsWithValue(t, "crypto/cipher: counter decreased or remained the same", func() { // TODO: the error message has changed between fips module versions, best way to verify it?
// assert.PanicsWithValue(t, "crypto/cipher: counter decreased", func() {
// assert.PanicsWithValue(t, "crypto/cipher: counter decreased or remained the same", func() {
assert.Panics(t, func() {
dst = aead.Seal([]byte{}, iv, plaintext, aad) dst = aead.Seal([]byte{}, iv, plaintext, aad)
}) })
} }
+12 -2
View File
@@ -4,10 +4,20 @@ package noiseutil
import ( import (
"crypto/fips140" "crypto/fips140"
"github.com/flynn/noise"
) )
// EncryptLockNeeded indicates if calls to Encrypt need a lock // EncryptLockNeeded indicates if calls to Encrypt need a lock
var EncryptLockNeeded = fips140.Enabled() var EncryptLockNeeded = fips140.Enabled()
// CipherAESGCM is the standard noise.CipherAESGCM when boringcrypto is not enabled var CipherAESGCM noise.CipherFunc = initAESGCM()
// var CipherAESGCM noise.CipherFunc = noise.CipherAESGCM
func initAESGCM() noise.CipherFunc {
if fips140.Enabled() {
return CipherAESGCMFIPS140
} else {
return noise.CipherAESGCM
}
}