diff --git a/pki.go b/pki.go index b63cb4e3..1dc4a68b 100644 --- a/pki.go +++ b/pki.go @@ -238,11 +238,15 @@ func (cs *CertState) getCertificate(v cert.Version) cert.Certificate { return nil } -func newCipherSuite(curve cert.Curve, pkcs11backed bool, cipher string) (noise.CipherSuite, error) { +// newCipherSuite builds the noise.CipherSuite for the given curve and cipher. +// When fips140Enforced is true (FIPS 140-only mode), non-approved algorithms +// (Curve25519 and ChaChaPoly) are rejected with an error. Callers pass +// fips140.Enforced() for fips140Enforced. +func newCipherSuite(curve cert.Curve, pkcs11backed bool, cipher string, fips140Enforced bool) (noise.CipherSuite, error) { var dhFunc noise.DHFunc switch curve { case cert.Curve_CURVE25519: - if fips140.Enforced() { + if fips140Enforced { return nil, errors.New("pki: use of Curve25519 is not allowed in FIPS 140-only mode") } dhFunc = noise.DH25519 @@ -257,7 +261,7 @@ func newCipherSuite(curve cert.Curve, pkcs11backed bool, cipher string) (noise.C } if cipher == "chachapoly" { - if fips140.Enforced() { + if fips140Enforced { return nil, errors.New("pki: use of ChaChaPoly is not allowed in FIPS 140-only mode") } return noise.NewCipherSuite(dhFunc, noise.CipherChaChaPoly, noise.HashSHA256), nil @@ -420,7 +424,7 @@ func newCertState(dv cert.Version, v1, v2 cert.Certificate, pkcs11backed bool, p if err != nil { return nil, fmt.Errorf("error marshalling v1 certificate for handshake: %w", err) } - ncs, err := newCipherSuite(v1.Curve(), pkcs11backed, cipher) + ncs, err := newCipherSuite(v1.Curve(), pkcs11backed, cipher, fips140.Enforced()) if err != nil { return nil, err } @@ -445,7 +449,7 @@ func newCertState(dv cert.Version, v1, v2 cert.Certificate, pkcs11backed bool, p if err != nil { return nil, fmt.Errorf("error marshalling v2 certificate for handshake: %w", err) } - ncs, err := newCipherSuite(v2.Curve(), pkcs11backed, cipher) + ncs, err := newCipherSuite(v2.Curve(), pkcs11backed, cipher, fips140.Enforced()) if err != nil { return nil, err } diff --git a/pki_test.go b/pki_test.go new file mode 100644 index 00000000..f90ff2ca --- /dev/null +++ b/pki_test.go @@ -0,0 +1,93 @@ +package nebula + +import ( + "strings" + "testing" + + "github.com/slackhq/nebula/cert" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestNewCipherSuite(t *testing.T) { + tests := []struct { + name string + curve cert.Curve + cipher string + fips140Enforced bool + wantErr string + // wantName is a substring expected in the resulting CipherSuite name + // (e.g. "P256" or "AESGCM"), only checked when wantErr is empty. + wantName string + }{ + { + name: "curve25519 aesgcm, not enforced", + curve: cert.Curve_CURVE25519, + cipher: "aesgcm", + wantName: "AESGCM", + }, + { + name: "curve25519 chachapoly, not enforced", + curve: cert.Curve_CURVE25519, + cipher: "chachapoly", + wantName: "ChaChaPoly", + }, + { + name: "p256 aesgcm, not enforced", + curve: cert.Curve_P256, + cipher: "aesgcm", + wantName: "P256", + }, + { + name: "p256 aesgcm, enforced is allowed", + curve: cert.Curve_P256, + cipher: "aesgcm", + fips140Enforced: true, + wantName: "P256", + }, + { + name: "curve25519 rejected when enforced", + curve: cert.Curve_CURVE25519, + cipher: "aesgcm", + fips140Enforced: true, + wantErr: "pki: use of Curve25519 is not allowed in FIPS 140-only mode", + }, + { + name: "chachapoly rejected when enforced", + curve: cert.Curve_P256, + cipher: "chachapoly", + fips140Enforced: true, + wantErr: "pki: use of ChaChaPoly is not allowed in FIPS 140-only mode", + }, + { + // Curve is checked before cipher, so a Curve25519+ChaChaPoly + // request reports the Curve25519 rejection. + name: "curve25519 chachapoly rejected on curve when enforced", + curve: cert.Curve_CURVE25519, + cipher: "chachapoly", + fips140Enforced: true, + wantErr: "pki: use of Curve25519 is not allowed in FIPS 140-only mode", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + cs, err := newCipherSuite(tt.curve, false, tt.cipher, tt.fips140Enforced) + if tt.wantErr != "" { + require.EqualError(t, err, tt.wantErr) + assert.Nil(t, cs) + return + } + require.NoError(t, err) + require.NotNil(t, cs) + assert.Contains(t, string(cs.Name()), tt.wantName) + }) + } +} + +func TestNewCipherSuiteUnsupportedCurve(t *testing.T) { + cs, err := newCipherSuite(cert.Curve(99), false, "aesgcm", false) + require.Error(t, err) + assert.True(t, strings.HasPrefix(err.Error(), "unsupported curve:"), "got: %v", err) + assert.Nil(t, cs) +}