3
0
Эх сурвалжийг харах

WIP support new Go fips140 module

This will replace boring crypto at some point.

We should modify our protocol a bit and instead change to
NewGCMWithRandomNonce.
Wade Simmons 5 сар өмнө
parent
commit
4485c47641

+ 2 - 2
go.mod

@@ -1,8 +1,8 @@
 module github.com/slackhq/nebula
 module github.com/slackhq/nebula
 
 
-go 1.23.6
+go 1.24.0
 
 
-toolchain go1.23.7
+toolchain go1.24.1
 
 
 require (
 require (
 	dario.cat/mergo v1.0.1
 	dario.cat/mergo v1.0.1

+ 64 - 0
noiseutil/fips140.go

@@ -0,0 +1,64 @@
+//go:build fips140
+// +build fips140
+
+package noiseutil
+
+import (
+	"crypto/cipher"
+	"encoding/binary"
+
+	// unsafe needed for go:linkname
+	_ "unsafe"
+
+	"github.com/flynn/noise"
+)
+
+// EncryptLockNeeded indicates if calls to Encrypt need a lock
+// This is true for boringcrypto because the Seal function verifies that the
+// nonce is strictly increasing.
+const EncryptLockNeeded = true
+
+//go:linkname aeadAESGCM crypto/tls.aeadAESGCM
+func aeadAESGCM(key, noncePrefix []byte) cipher.AEAD
+
+type cipherFn struct {
+	fn   func([32]byte) noise.Cipher
+	name string
+}
+
+func (c cipherFn) Cipher(k [32]byte) noise.Cipher { return c.fn(k) }
+func (c cipherFn) CipherName() string             { return c.name }
+
+// CipherAESGCM is the AES256-GCM AEAD cipher (using aeadAESGCM when fips140 is enabled)
+var CipherAESGCM noise.CipherFunc = cipherFn{cipherAESGCM, "AESGCM"}
+
+var emptyPrefix = []byte{0, 0, 0, 0}
+
+func cipherAESGCM(k [32]byte) noise.Cipher {
+	// c, err := aes.NewCipher(k[:])
+	// if err != nil {
+	// 	panic(err)
+	// }
+	gcm := aeadAESGCM(k[:], emptyPrefix)
+	return aeadCipher{
+		gcm,
+		func(n uint64) []byte {
+			var nonce [12]byte
+			binary.BigEndian.PutUint64(nonce[4:], n)
+			return nonce[:]
+		},
+	}
+}
+
+type aeadCipher struct {
+	cipher.AEAD
+	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)
+}
+
+func (c aeadCipher) Decrypt(out []byte, n uint64, ad, ciphertext []byte) ([]byte, error) {
+	return c.Open(out, c.nonce(n), ciphertext, ad)
+}

+ 42 - 0
noiseutil/fips140_test.go

@@ -0,0 +1,42 @@
+//go:build fips140
+// +build fips140
+
+package noiseutil
+
+import (
+	"crypto/fips140"
+	"encoding/hex"
+	"log"
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestEncryptLockNeeded(t *testing.T) {
+	assert.True(t, EncryptLockNeeded)
+}
+
+// Ensure NewAESGCM validates the nonce is non-repeating
+func TestNewAESGCM(t *testing.T) {
+	assert.True(t, fips140.Enabled())
+
+	key, _ := hex.DecodeString("feffe9928665731c6d6a8f9467308308feffe9928665731c6d6a8f9467308308")
+	iv, _ := hex.DecodeString("00000000facedbaddecaf888")
+	plaintext, _ := hex.DecodeString("d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39")
+	aad, _ := hex.DecodeString("feedfacedeadbeeffeedfacedeadbeefabaddad2")
+	expected, _ := hex.DecodeString("72ce2ea385f88c20d856e9d1248c2ca08562bbe8a61459ffae06ec393540518e9b6b4c40a146053f26a3df83c5384a48d273148b15aba64d970107432b2892741359275676441c1572c3fa9e")
+
+	var keyArray [32]byte
+	copy(keyArray[:], key)
+	c := CipherAESGCM.Cipher(keyArray)
+	aead := c.(aeadCipher).AEAD
+
+	dst := aead.Seal([]byte{}, iv, plaintext, aad)
+	log.Printf("%x", dst)
+	assert.Equal(t, expected, dst)
+
+	// We expect this to fail since we are re-encrypting with a repeat IV
+	assert.PanicsWithValue(t, "crypto/cipher: counter decreased", func() {
+		dst = aead.Seal([]byte{}, iv, plaintext, aad)
+	})
+}

+ 2 - 2
noiseutil/notboring.go

@@ -1,5 +1,5 @@
-//go:build !boringcrypto
-// +build !boringcrypto
+//go:build !boringcrypto && !fips140
+// +build !boringcrypto,!fips140
 
 
 package noiseutil
 package noiseutil
 
 

+ 2 - 2
noiseutil/notboring_test.go

@@ -1,5 +1,5 @@
-//go:build !boringcrypto
-// +build !boringcrypto
+//go:build !boringcrypto && !fips140
+// +build !boringcrypto,!fips140
 
 
 package noiseutil
 package noiseutil