diff options
Diffstat (limited to 'src/crypto/tls')
-rw-r--r-- | src/crypto/tls/auth.go | 4 | ||||
-rw-r--r-- | src/crypto/tls/auth_test.go | 2 | ||||
-rw-r--r-- | src/crypto/tls/boring.go | 127 | ||||
-rw-r--r-- | src/crypto/tls/boring_test.go | 630 | ||||
-rw-r--r-- | src/crypto/tls/cipher_suites.go | 22 | ||||
-rw-r--r-- | src/crypto/tls/common.go | 13 | ||||
-rw-r--r-- | src/crypto/tls/fipsonly/fipsonly.go | 27 | ||||
-rw-r--r-- | src/crypto/tls/fipsonly/fipsonly_test.go | 16 | ||||
-rw-r--r-- | src/crypto/tls/handshake_client.go | 7 | ||||
-rw-r--r-- | src/crypto/tls/handshake_client_tls13.go | 6 | ||||
-rw-r--r-- | src/crypto/tls/handshake_messages_test.go | 8 | ||||
-rw-r--r-- | src/crypto/tls/handshake_server.go | 4 | ||||
-rw-r--r-- | src/crypto/tls/handshake_server_tls13.go | 8 |
13 files changed, 860 insertions, 14 deletions
diff --git a/src/crypto/tls/auth.go b/src/crypto/tls/auth.go index a9df0da6d62..7c5675c6d93 100644 --- a/src/crypto/tls/auth.go +++ b/src/crypto/tls/auth.go @@ -169,6 +169,7 @@ var rsaSignatureSchemes = []struct { // and optionally filtered by its explicit SupportedSignatureAlgorithms. // // This function must be kept in sync with supportedSignatureAlgorithms. +// FIPS filtering is applied in the caller, selectSignatureScheme. func signatureSchemesForCertificate(version uint16, cert *Certificate) []SignatureScheme { priv, ok := cert.PrivateKey.(crypto.Signer) if !ok { @@ -241,6 +242,9 @@ func selectSignatureScheme(vers uint16, c *Certificate, peerAlgs []SignatureSche // Pick signature scheme in the peer's preference order, as our // preference order is not configurable. for _, preferredAlg := range peerAlgs { + if needFIPS() && !isSupportedSignatureAlgorithm(preferredAlg, fipsSupportedSignatureAlgorithms) { + continue + } if isSupportedSignatureAlgorithm(preferredAlg, supportedAlgs) { return preferredAlg, nil } diff --git a/src/crypto/tls/auth_test.go b/src/crypto/tls/auth_test.go index c42e3491e46..c23d93f3c08 100644 --- a/src/crypto/tls/auth_test.go +++ b/src/crypto/tls/auth_test.go @@ -153,7 +153,7 @@ func TestLegacyTypeAndHash(t *testing.T) { // TestSupportedSignatureAlgorithms checks that all supportedSignatureAlgorithms // have valid type and hash information. func TestSupportedSignatureAlgorithms(t *testing.T) { - for _, sigAlg := range supportedSignatureAlgorithms { + for _, sigAlg := range supportedSignatureAlgorithms() { sigType, hash, err := typeAndHashFromSignatureScheme(sigAlg) if err != nil { t.Errorf("%v: unexpected error: %v", sigAlg, err) diff --git a/src/crypto/tls/boring.go b/src/crypto/tls/boring.go new file mode 100644 index 00000000000..09f71c1691c --- /dev/null +++ b/src/crypto/tls/boring.go @@ -0,0 +1,127 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package tls + +import ( + "crypto/ecdsa" + "crypto/internal/boring/fipstls" + "crypto/rsa" + "crypto/x509" +) + +// needFIPS returns fipstls.Required(); it avoids a new import in common.go. +func needFIPS() bool { + return fipstls.Required() +} + +// fipsMinVersion replaces c.minVersion in FIPS-only mode. +func fipsMinVersion(c *Config) uint16 { + // FIPS requires TLS 1.2. + return VersionTLS12 +} + +// fipsMaxVersion replaces c.maxVersion in FIPS-only mode. +func fipsMaxVersion(c *Config) uint16 { + // FIPS requires TLS 1.2. + return VersionTLS12 +} + +// default defaultFIPSCurvePreferences is the FIPS-allowed curves, +// in preference order (most preferable first). +var defaultFIPSCurvePreferences = []CurveID{CurveP256, CurveP384, CurveP521} + +// fipsCurvePreferences replaces c.curvePreferences in FIPS-only mode. +func fipsCurvePreferences(c *Config) []CurveID { + if c == nil || len(c.CurvePreferences) == 0 { + return defaultFIPSCurvePreferences + } + var list []CurveID + for _, id := range c.CurvePreferences { + for _, allowed := range defaultFIPSCurvePreferences { + if id == allowed { + list = append(list, id) + break + } + } + } + return list +} + +// defaultCipherSuitesFIPS are the FIPS-allowed cipher suites. +var defaultCipherSuitesFIPS = []uint16{ + TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, + TLS_RSA_WITH_AES_128_GCM_SHA256, + TLS_RSA_WITH_AES_256_GCM_SHA384, +} + +// fipsCipherSuites replaces c.cipherSuites in FIPS-only mode. +func fipsCipherSuites(c *Config) []uint16 { + if c == nil || c.CipherSuites == nil { + return defaultCipherSuitesFIPS + } + list := make([]uint16, 0, len(defaultCipherSuitesFIPS)) + for _, id := range c.CipherSuites { + for _, allowed := range defaultCipherSuitesFIPS { + if id == allowed { + list = append(list, id) + break + } + } + } + return list +} + +// isBoringCertificate reports whether a certificate may be used +// when constructing a verified chain. +// It is called for each leaf, intermediate, and root certificate. +func isBoringCertificate(c *x509.Certificate) bool { + if !needFIPS() { + // Everything is OK if we haven't forced FIPS-only mode. + return true + } + + // Otherwise the key must be RSA 2048, RSA 3072, or ECDSA P-256. + switch k := c.PublicKey.(type) { + default: + return false + case *rsa.PublicKey: + if size := k.N.BitLen(); size != 2048 && size != 3072 { + return false + } + case *ecdsa.PublicKey: + if name := k.Curve.Params().Name; name != "P-256" && name != "P-384" { + return false + } + } + + return true +} + +// fipsSupportedSignatureAlgorithms currently are a subset of +// defaultSupportedSignatureAlgorithms without Ed25519 and SHA-1. +var fipsSupportedSignatureAlgorithms = []SignatureScheme{ + PSSWithSHA256, + PSSWithSHA384, + PSSWithSHA512, + PKCS1WithSHA256, + ECDSAWithP256AndSHA256, + PKCS1WithSHA384, + ECDSAWithP384AndSHA384, + PKCS1WithSHA512, + ECDSAWithP521AndSHA512, +} + +// supportedSignatureAlgorithms returns the supported signature algorithms. +func supportedSignatureAlgorithms() []SignatureScheme { + if !needFIPS() { + return defaultSupportedSignatureAlgorithms + } + return fipsSupportedSignatureAlgorithms +} + +var testingOnlyForceClientHelloSignatureAlgorithms []SignatureScheme diff --git a/src/crypto/tls/boring_test.go b/src/crypto/tls/boring_test.go new file mode 100644 index 00000000000..6ad72faebf9 --- /dev/null +++ b/src/crypto/tls/boring_test.go @@ -0,0 +1,630 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package tls + +import ( + "crypto/ecdsa" + "crypto/elliptic" + "crypto/internal/boring/fipstls" + "crypto/rand" + "crypto/rsa" + "crypto/x509" + "crypto/x509/pkix" + "encoding/pem" + "fmt" + "math/big" + "net" + "runtime" + "strings" + "testing" + "time" +) + +func TestBoringServerProtocolVersion(t *testing.T) { + test := func(name string, v uint16, msg string) { + t.Run(name, func(t *testing.T) { + serverConfig := testConfig.Clone() + serverConfig.MinVersion = VersionSSL30 + clientHello := &clientHelloMsg{ + vers: v, + random: make([]byte, 32), + cipherSuites: allCipherSuites(), + compressionMethods: []uint8{compressionNone}, + supportedVersions: []uint16{v}, + } + testClientHelloFailure(t, serverConfig, clientHello, msg) + }) + } + + test("VersionTLS10", VersionTLS10, "") + test("VersionTLS11", VersionTLS11, "") + test("VersionTLS12", VersionTLS12, "") + test("VersionTLS13", VersionTLS13, "") + + fipstls.Force() + defer fipstls.Abandon() + test("VersionSSL30", VersionSSL30, "client offered only unsupported versions") + test("VersionTLS10", VersionTLS10, "client offered only unsupported versions") + test("VersionTLS11", VersionTLS11, "client offered only unsupported versions") + test("VersionTLS12", VersionTLS12, "") + test("VersionTLS13", VersionTLS13, "client offered only unsupported versions") +} + +func isBoringVersion(v uint16) bool { + return v == VersionTLS12 +} + +func isBoringCipherSuite(id uint16) bool { + switch id { + case TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, + TLS_RSA_WITH_AES_128_GCM_SHA256, + TLS_RSA_WITH_AES_256_GCM_SHA384: + return true + } + return false +} + +func isBoringCurve(id CurveID) bool { + switch id { + case CurveP256, CurveP384, CurveP521: + return true + } + return false +} + +func isECDSA(id uint16) bool { + for _, suite := range cipherSuites { + if suite.id == id { + return suite.flags&suiteECSign == suiteECSign + } + } + panic(fmt.Sprintf("unknown cipher suite %#x", id)) +} + +func isBoringSignatureScheme(alg SignatureScheme) bool { + switch alg { + default: + return false + case PKCS1WithSHA256, + ECDSAWithP256AndSHA256, + PKCS1WithSHA384, + ECDSAWithP384AndSHA384, + PKCS1WithSHA512, + ECDSAWithP521AndSHA512, + PSSWithSHA256, + PSSWithSHA384, + PSSWithSHA512: + // ok + } + return true +} + +func TestBoringServerCipherSuites(t *testing.T) { + serverConfig := testConfig.Clone() + serverConfig.CipherSuites = allCipherSuites() + serverConfig.Certificates = make([]Certificate, 1) + + for _, id := range allCipherSuites() { + if isECDSA(id) { + serverConfig.Certificates[0].Certificate = [][]byte{testECDSACertificate} + serverConfig.Certificates[0].PrivateKey = testECDSAPrivateKey + } else { + serverConfig.Certificates[0].Certificate = [][]byte{testRSACertificate} + serverConfig.Certificates[0].PrivateKey = testRSAPrivateKey + } + serverConfig.BuildNameToCertificate() + t.Run(fmt.Sprintf("suite=%#x", id), func(t *testing.T) { + clientHello := &clientHelloMsg{ + vers: VersionTLS12, + random: make([]byte, 32), + cipherSuites: []uint16{id}, + compressionMethods: []uint8{compressionNone}, + supportedCurves: defaultCurvePreferences, + supportedPoints: []uint8{pointFormatUncompressed}, + } + + testClientHello(t, serverConfig, clientHello) + t.Run("fipstls", func(t *testing.T) { + fipstls.Force() + defer fipstls.Abandon() + msg := "" + if !isBoringCipherSuite(id) { + msg = "no cipher suite supported by both client and server" + } + testClientHelloFailure(t, serverConfig, clientHello, msg) + }) + }) + } +} + +func TestBoringServerCurves(t *testing.T) { + serverConfig := testConfig.Clone() + serverConfig.Certificates = make([]Certificate, 1) + serverConfig.Certificates[0].Certificate = [][]byte{testECDSACertificate} + serverConfig.Certificates[0].PrivateKey = testECDSAPrivateKey + serverConfig.BuildNameToCertificate() + + for _, curveid := range defaultCurvePreferences { + t.Run(fmt.Sprintf("curve=%d", curveid), func(t *testing.T) { + clientHello := &clientHelloMsg{ + vers: VersionTLS12, + random: make([]byte, 32), + cipherSuites: []uint16{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256}, + compressionMethods: []uint8{compressionNone}, + supportedCurves: []CurveID{curveid}, + supportedPoints: []uint8{pointFormatUncompressed}, + } + + testClientHello(t, serverConfig, clientHello) + + // With fipstls forced, bad curves should be rejected. + t.Run("fipstls", func(t *testing.T) { + fipstls.Force() + defer fipstls.Abandon() + msg := "" + if !isBoringCurve(curveid) { + msg = "no cipher suite supported by both client and server" + } + testClientHelloFailure(t, serverConfig, clientHello, msg) + }) + }) + } +} + +func boringHandshake(t *testing.T, clientConfig, serverConfig *Config) (clientErr, serverErr error) { + c, s := localPipe(t) + client := Client(c, clientConfig) + server := Server(s, serverConfig) + done := make(chan error, 1) + go func() { + done <- client.Handshake() + c.Close() + }() + serverErr = server.Handshake() + s.Close() + clientErr = <-done + return +} + +func TestBoringServerSignatureAndHash(t *testing.T) { + defer func() { + testingOnlyForceClientHelloSignatureAlgorithms = nil + }() + + for _, sigHash := range defaultSupportedSignatureAlgorithms { + t.Run(fmt.Sprintf("%#x", sigHash), func(t *testing.T) { + serverConfig := testConfig.Clone() + serverConfig.Certificates = make([]Certificate, 1) + + testingOnlyForceClientHelloSignatureAlgorithms = []SignatureScheme{sigHash} + + sigType, _, _ := typeAndHashFromSignatureScheme(sigHash) + switch sigType { + case signaturePKCS1v15, signatureRSAPSS: + serverConfig.CipherSuites = []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256} + serverConfig.Certificates[0].Certificate = [][]byte{testRSA2048Certificate} + serverConfig.Certificates[0].PrivateKey = testRSA2048PrivateKey + case signatureEd25519: + serverConfig.CipherSuites = []uint16{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256} + serverConfig.Certificates[0].Certificate = [][]byte{testEd25519Certificate} + serverConfig.Certificates[0].PrivateKey = testEd25519PrivateKey + case signatureECDSA: + serverConfig.CipherSuites = []uint16{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256} + serverConfig.Certificates[0].Certificate = [][]byte{testECDSACertificate} + serverConfig.Certificates[0].PrivateKey = testECDSAPrivateKey + } + serverConfig.BuildNameToCertificate() + // PKCS#1 v1.5 signature algorithms can't be used standalone in TLS + // 1.3, and the ECDSA ones bind to the curve used. + serverConfig.MaxVersion = VersionTLS12 + + clientErr, serverErr := boringHandshake(t, testConfig, serverConfig) + if clientErr != nil { + t.Fatalf("expected handshake with %#x to succeed; client error: %v; server error: %v", sigHash, clientErr, serverErr) + } + + // With fipstls forced, bad curves should be rejected. + t.Run("fipstls", func(t *testing.T) { + fipstls.Force() + defer fipstls.Abandon() + clientErr, _ := boringHandshake(t, testConfig, serverConfig) + if isBoringSignatureScheme(sigHash) { + if clientErr != nil { + t.Fatalf("expected handshake with %#x to succeed; err=%v", sigHash, clientErr) + } + } else { + if clientErr == nil { + t.Fatalf("expected handshake with %#x to fail, but it succeeded", sigHash) + } + } + }) + }) + } +} + +func TestBoringClientHello(t *testing.T) { + // Test that no matter what we put in the client config, + // the client does not offer non-FIPS configurations. + fipstls.Force() + defer fipstls.Abandon() + + c, s := net.Pipe() + defer c.Close() + defer s.Close() + + clientConfig := testConfig.Clone() + // All sorts of traps for the client to avoid. + clientConfig.MinVersion = VersionSSL30 + clientConfig.MaxVersion = VersionTLS13 + clientConfig.CipherSuites = allCipherSuites() + clientConfig.CurvePreferences = defaultCurvePreferences + + go Client(c, testConfig).Handshake() + srv := Server(s, testConfig) + msg, err := srv.readHandshake() + if err != nil { + t.Fatal(err) + } + hello, ok := msg.(*clientHelloMsg) + if !ok { + t.Fatalf("unexpected message type %T", msg) + } + + if !isBoringVersion(hello.vers) { + t.Errorf("client vers=%#x, want %#x (TLS 1.2)", hello.vers, VersionTLS12) + } + for _, v := range hello.supportedVersions { + if !isBoringVersion(v) { + t.Errorf("client offered disallowed version %#x", v) + } + } + for _, id := range hello.cipherSuites { + if !isBoringCipherSuite(id) { + t.Errorf("client offered disallowed suite %#x", id) + } + } + for _, id := range hello.supportedCurves { + if !isBoringCurve(id) { + t.Errorf("client offered disallowed curve %d", id) + } + } + for _, sigHash := range hello.supportedSignatureAlgorithms { + if !isBoringSignatureScheme(sigHash) { + t.Errorf("client offered disallowed signature-and-hash %v", sigHash) + } + } +} + +func TestBoringCertAlgs(t *testing.T) { + // NaCl, arm and wasm time out generating keys. Nothing in this test is architecture-specific, so just don't bother on those. + if runtime.GOOS == "nacl" || runtime.GOARCH == "arm" || runtime.GOOS == "js" { + t.Skipf("skipping on %s/%s because key generation takes too long", runtime.GOOS, runtime.GOARCH) + } + + // Set up some roots, intermediate CAs, and leaf certs with various algorithms. + // X_Y is X signed by Y. + R1 := boringCert(t, "R1", boringRSAKey(t, 2048), nil, boringCertCA|boringCertFIPSOK) + R2 := boringCert(t, "R2", boringRSAKey(t, 4096), nil, boringCertCA) + + M1_R1 := boringCert(t, "M1_R1", boringECDSAKey(t, elliptic.P256()), R1, boringCertCA|boringCertFIPSOK) + M2_R1 := boringCert(t, "M2_R1", boringECDSAKey(t, elliptic.P224()), R1, boringCertCA) + + I_R1 := boringCert(t, "I_R1", boringRSAKey(t, 3072), R1, boringCertCA|boringCertFIPSOK) + I_R2 := boringCert(t, "I_R2", I_R1.key, R2, boringCertCA|boringCertFIPSOK) + I_M1 := boringCert(t, "I_M1", I_R1.key, M1_R1, boringCertCA|boringCertFIPSOK) + I_M2 := boringCert(t, "I_M2", I_R1.key, M2_R1, boringCertCA|boringCertFIPSOK) + + L1_I := boringCert(t, "L1_I", boringECDSAKey(t, elliptic.P384()), I_R1, boringCertLeaf|boringCertFIPSOK) + L2_I := boringCert(t, "L2_I", boringRSAKey(t, 1024), I_R1, boringCertLeaf) + + // boringCert checked that isBoringCertificate matches the caller's boringCertFIPSOK bit. + // If not, no point in building bigger end-to-end tests. + if t.Failed() { + t.Fatalf("isBoringCertificate failures; not continuing") + } + + // client verifying server cert + testServerCert := func(t *testing.T, desc string, pool *x509.CertPool, key interface{}, list [][]byte, ok bool) { + clientConfig := testConfig.Clone() + clientConfig.RootCAs = pool + clientConfig.InsecureSkipVerify = false + clientConfig.ServerName = "example.com" + + serverConfig := testConfig.Clone() + serverConfig.Certificates = []Certificate{{Certificate: list, PrivateKey: key}} + serverConfig.BuildNameToCertificate() + + clientErr, _ := boringHandshake(t, clientConfig, serverConfig) + + if (clientErr == nil) == ok { + if ok { + t.Logf("%s: accept", desc) + } else { + t.Logf("%s: reject", desc) + } + } else { + if ok { + t.Errorf("%s: BAD reject (%v)", desc, clientErr) + } else { + t.Errorf("%s: BAD accept", desc) + } + } + } + + // server verifying client cert + testClientCert := func(t *testing.T, desc string, pool *x509.CertPool, key interface{}, list [][]byte, ok bool) { + clientConfig := testConfig.Clone() + clientConfig.ServerName = "example.com" + clientConfig.Certificates = []Certificate{{Certificate: list, PrivateKey: key}} + + serverConfig := testConfig.Clone() + serverConfig.ClientCAs = pool + serverConfig.ClientAuth = RequireAndVerifyClientCert + + _, serverErr := boringHandshake(t, clientConfig, serverConfig) + + if (serverErr == nil) == ok { + if ok { + t.Logf("%s: accept", desc) + } else { + t.Logf("%s: reject", desc) + } + } else { + if ok { + t.Errorf("%s: BAD reject (%v)", desc, serverErr) + } else { + t.Errorf("%s: BAD accept", desc) + } + } + } + + // Run simple basic test with known answers before proceeding to + // exhaustive test with computed answers. + r1pool := x509.NewCertPool() + r1pool.AddCert(R1.cert) + testServerCert(t, "basic", r1pool, L2_I.key, [][]byte{L2_I.der, I_R1.der}, true) + testClientCert(t, "basic (client cert)", r1pool, L2_I.key, [][]byte{L2_I.der, I_R1.der}, true) + fipstls.Force() + testServerCert(t, "basic (fips)", r1pool, L2_I.key, [][]byte{L2_I.der, I_R1.der}, false) + testClientCert(t, "basic (fips, client cert)", r1pool, L2_I.key, [][]byte{L2_I.der, I_R1.der}, false) + fipstls.Abandon() + + if t.Failed() { + t.Fatal("basic test failed, skipping exhaustive test") + } + + if testing.Short() { + t.Logf("basic test passed; skipping exhaustive test in -short mode") + return + } + + for l := 1; l <= 2; l++ { + leaf := L1_I + if l == 2 { + leaf = L2_I + } + for i := 0; i < 64; i++ { + reachable := map[string]bool{leaf.parentOrg: true} + reachableFIPS := map[string]bool{leaf.parentOrg: leaf.fipsOK} + list := [][]byte{leaf.der} + listName := leaf.name + addList := func(cond int, c *boringCertificate) { + if cond != 0 { + list = append(list, c.der) + listName += "," + c.name + if reachable[c.org] { + reachable[c.parentOrg] = true + } + if reachableFIPS[c.org] && c.fipsOK { + reachableFIPS[c.parentOrg] = true + } + } + } + addList(i&1, I_R1) + addList(i&2, I_R2) + addList(i&4, I_M1) + addList(i&8, I_M2) + addList(i&16, M1_R1) + addList(i&32, M2_R1) + + for r := 1; r <= 3; r++ { + pool := x509.NewCertPool() + rootName := "," + shouldVerify := false + shouldVerifyFIPS := false + addRoot := func(cond int, c *boringCertificate) { + if cond != 0 { + rootName += "," + c.name + pool.AddCert(c.cert) + if reachable[c.org] { + shouldVerify = true + } + if reachableFIPS[c.org] && c.fipsOK { + shouldVerifyFIPS = true + } + } + } + addRoot(r&1, R1) + addRoot(r&2, R2) + rootName = rootName[1:] // strip leading comma + testServerCert(t, listName+"->"+rootName[1:], pool, leaf.key, list, shouldVerify) + testClientCert(t, listName+"->"+rootName[1:]+"(client cert)", pool, leaf.key, list, shouldVerify) + fipstls.Force() + testServerCert(t, listName+"->"+rootName[1:]+" (fips)", pool, leaf.key, list, shouldVerifyFIPS) + testClientCert(t, listName+"->"+rootName[1:]+" (fips, client cert)", pool, leaf.key, list, shouldVerifyFIPS) + fipstls.Abandon() + } + } + } +} + +const ( + boringCertCA = iota + boringCertLeaf + boringCertFIPSOK = 0x80 +) + +func boringRSAKey(t *testing.T, size int) *rsa.PrivateKey { + k, err := rsa.GenerateKey(rand.Reader, size) + if err != nil { + t.Fatal(err) + } + return k +} + +func boringECDSAKey(t *testing.T, curve elliptic.Curve) *ecdsa.PrivateKey { + k, err := ecdsa.GenerateKey(curve, rand.Reader) + if err != nil { + t.Fatal(err) + } + return k +} + +type boringCertificate struct { + name string + org string + parentOrg string + der []byte + cert *x509.Certificate + key interface{} + fipsOK bool +} + +func boringCert(t *testing.T, name string, key interface{}, parent *boringCertificate, mode int) *boringCertificate { + org := name + parentOrg := "" + if i := strings.Index(org, "_"); i >= 0 { + org = org[:i] + parentOrg = name[i+1:] + } + tmpl := &x509.Certificate{ + SerialNumber: big.NewInt(1), + Subject: pkix.Name{ + Organization: []string{org}, + }, + NotBefore: time.Unix(0, 0), + NotAfter: time.Unix(0, 0), + + KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth}, + BasicConstraintsValid: true, + } + if mode&^boringCertFIPSOK == boringCertLeaf { + tmpl.DNSNames = []string{"example.com"} + } else { + tmpl.IsCA = true + tmpl.KeyUsage |= x509.KeyUsageCertSign + } + + var pcert *x509.Certificate + var pkey interface{} + if parent != nil { + pcert = parent.cert + pkey = parent.key + } else { + pcert = tmpl + pkey = key + } + + var pub interface{} + var desc string + switch k := key.(type) { + case *rsa.PrivateKey: + pub = &k.PublicKey + desc = fmt.Sprintf("RSA-%d", k.N.BitLen()) + case *ecdsa.PrivateKey: + pub = &k.PublicKey + desc = "ECDSA-" + k.Curve.Params().Name + default: + t.Fatalf("invalid key %T", key) + } + + der, err := x509.CreateCertificate(rand.Reader, tmpl, pcert, pub, pkey) + if err != nil { + t.Fatal(err) + } + cert, err := x509.ParseCertificate(der) + if err != nil { + t.Fatal(err) + } + + // Tell isBoringCertificate to enforce FIPS restrictions for this check. + fipstls.Force() + defer fipstls.Abandon() + + fipsOK := mode&boringCertFIPSOK != 0 + if isBoringCertificate(cert) != fipsOK { + t.Errorf("isBoringCertificate(cert with %s key) = %v, want %v", desc, !fipsOK, fipsOK) + } + return &boringCertificate{name, org, parentOrg, der, cert, key, fipsOK} +} + +// A self-signed test certificate with an RSA key of size 2048, for testing +// RSA-PSS with SHA512. SAN of example.golang. +var ( + testRSA2048Certificate []byte + testRSA2048PrivateKey *rsa.PrivateKey +) + +func init() { + block, _ := pem.Decode([]byte(` +-----BEGIN CERTIFICATE----- +MIIC/zCCAeegAwIBAgIRALHHX/kh4+4zMU9DarzBEcQwDQYJKoZIhvcNAQELBQAw +EjEQMA4GA1UEChMHQWNtZSBDbzAeFw0xMTAxMDExNTA0MDVaFw0yMDEyMjkxNTA0 +MDVaMBIxEDAOBgNVBAoTB0FjbWUgQ28wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw +ggEKAoIBAQCf8fk0N6ieCBX4IOVIfKitt4kGcOQLeimCfsjqqHcysMIVGEtFSM6E +4Ay141f/7IqdW0UtIqNb4PXhROID7yDxR284xL6XbCuv/t5hP3UcehYc3hmLiyVd +MkZQiZWtfUUJf/1qOtM+ohNg59LRWp4d+6iX0la1JL3EwCIckkNjJ9hQbF7Pb2CS ++ES9Yo55KAap8KOblpcR8MBSN38bqnwjfQdCXvOEOjam2HUxKzEFX5MA+fA0me4C +ioCcCRLWKl+GoN9F8fABfoZ+T+2eal4DLuO95rXR8SrOIVBh3XFOr/RVhjtXcNVF +ZKcvDt6d68V6jAKAYKm5nlj9GPpd4v+rAgMBAAGjUDBOMA4GA1UdDwEB/wQEAwIF +oDATBgNVHSUEDDAKBggrBgEFBQcDATAMBgNVHRMBAf8EAjAAMBkGA1UdEQQSMBCC +DmV4YW1wbGUuZ29sYW5nMA0GCSqGSIb3DQEBCwUAA4IBAQCOoYsVcFCBhboqe3WH +dC6V7XXXECmnjh01r8h80yv0NR379nSD3cw2M+HKvaXysWqrl5hjGVKw0vtwD81r +V4JzDu7IfIog5m8+QNC+7LqDZsz88vDKOrsoySVOmUCgmCKFXew+LA+eO/iQEJTr +7ensddOeXJEp27Ed5vW+kmWW3Qmglc2Gwy8wFrMDIqnrnOzBA4oCnDEgtXJt0zog +nRwbfEMAWi1aQRy5dT9KA3SP9mo5SeTFSzGGHiE4s4gHUe7jvsAFF2qgtD6+wH6s +z9b6shxnC7g5IlBKhI7SVB/Uqt2ydJ+kH1YbjMcIq6NAM5eNMKgZuJr3+zwsSgwh +GNaE +-----END CERTIFICATE-----`)) + testRSA2048Certificate = block.Bytes + + block, _ = pem.Decode([]byte(` +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEAn/H5NDeonggV+CDlSHyorbeJBnDkC3opgn7I6qh3MrDCFRhL +RUjOhOAMteNX/+yKnVtFLSKjW+D14UTiA+8g8UdvOMS+l2wrr/7eYT91HHoWHN4Z +i4slXTJGUImVrX1FCX/9ajrTPqITYOfS0VqeHfuol9JWtSS9xMAiHJJDYyfYUGxe +z29gkvhEvWKOeSgGqfCjm5aXEfDAUjd/G6p8I30HQl7zhDo2pth1MSsxBV+TAPnw +NJnuAoqAnAkS1ipfhqDfRfHwAX6Gfk/tnmpeAy7jvea10fEqziFQYd1xTq/0VYY7 +V3DVRWSnLw7enevFeowCgGCpuZ5Y/Rj6XeL/qwIDAQABAoIBAQCNpMZifd/vg42h +HdCvLuZaYS0R7SunFlpoXEsltGdLFsnp0IfoJZ/ugFQBSAIIfLwMumU6oXA1z7Uv +98aIYV61DePrTCDVDFBsHbNmP8JAo8WtbusEbwd5zyoB7LYG2+clkJklWE73KqUq +rmI+UJeyScl2Gin7ZTxBXz1WPBk9VwcnwkeaXpgASIBW23fhECM9gnYEEwaBez5T +6Me8d1tHtYQv7vsKe7ro9w9/HKrRXejqYKK1LxkhfFriyV+m8LZJZn2nXOa6G3gF +Nb8Qk1Uk5PUBENBmyMFJhT4M/uuSq4YtMrrO2gi8Q+fPhuGzc5SshYKRBp0W4P5r +mtVCtEFRAoGBAMENBIFLrV2+HsGj0xYFasKov/QPe6HSTR1Hh2IZONp+oK4oszWE +jBT4VcnITmpl6tC1Wy4GcrxjNgKIFZAj+1x1LUULdorXkuG8yr0tAhG9zNyfWsSy +PrSovC0UVbzr8Jxxla+kQVxEQQqWQxPlEVuL8kXaIDA6Lyt1Hpua2LvPAoGBANQZ +c6Lq2T7+BxLxNdi2m8kZzej5kgzBp/XdVsbFWRlebIX2KrFHsrHzT9PUk3DE1vZK +M6pzTt94nQhWSkDgCaw1SohElJ3HFIFwcusF1SJAc3pQepd8ug6IYdlpDMLtBj/P +/5P6BVUtgo05E4+I/T3iYatmglQxTtlZ0RkSV2llAoGBALOXkKFX7ahPvf0WksDh +uTfuFOTPoowgQG0EpgW0wRdCxeg/JLic3lSD0gsttQV2WsRecryWcxaelRg10RmO +38BbogmhaF4xvgsSvujOfiZTE8oK1T43M+6NKsIlML3YILbpU/9aJxPWy0s2DqDr +cQJhZrlk+pzjBA7Bnf/URdwxAoGAKR/CNw14D+mrL3YLbbiCXiydqxVwxv5pdZdz +8thi3TNcsWC4iGURdcVqbfUinVPdJiXe/Kac3WGCeRJaFVgbKAOxLti1RB5MkIhg +D8eyupBqk4W1L1gkrxqsdj4TFlxkwMywjl2E2S4YyQ8PBt6V04DoVRZsIKzqz+PF +UionPq0CgYBCYXvqioJhPewkOq/Y5wrDBeZW1FQK5QD9W5M8/5zxd4rdvJtjhbJp +oOrtvMdrl6upy9Hz4BJD3FXwVFiPFE7jqeNqi0F21viLxBPMMD3UODF6LL5EyLiR +9V4xVMS8KXxvg7rxsuqzMPscViaWUL6WNVBhsD2+92dHxSXzz5EJKQ== +-----END RSA PRIVATE KEY-----`)) + var err error + testRSA2048PrivateKey, err = x509.ParsePKCS1PrivateKey(block.Bytes) + if err != nil { + panic(err) + } +} diff --git a/src/crypto/tls/cipher_suites.go b/src/crypto/tls/cipher_suites.go index 4bf06468c6d..e07d742bd3b 100644 --- a/src/crypto/tls/cipher_suites.go +++ b/src/crypto/tls/cipher_suites.go @@ -4,6 +4,8 @@ package tls +import "crypto/internal/boring" + import ( "crypto" "crypto/aes" @@ -422,7 +424,13 @@ func cipherAES(key, iv []byte, isRead bool) interface{} { // macSHA1 returns a SHA-1 based constant time MAC. func macSHA1(key []byte) hash.Hash { - return hmac.New(newConstantTimeHash(sha1.New), key) + h := sha1.New + // The BoringCrypto SHA1 does not have a constant-time + // checksum function, so don't try to use it. + if !boring.Enabled { + h = newConstantTimeHash(h) + } + return hmac.New(h, key) } // macSHA256 returns a SHA-256 based MAC. This is only supported in TLS 1.2 and @@ -510,7 +518,16 @@ func aeadAESGCM(key, noncePrefix []byte) aead { if err != nil { panic(err) } - aead, err := cipher.NewGCM(aes) + type gcmtls interface { + NewGCMTLS() (cipher.AEAD, error) + } + var aead cipher.AEAD + if aesTLS, ok := aes.(gcmtls); ok { + aead, err = aesTLS.NewGCMTLS() + } else { + boring.Unreachable() + aead, err = cipher.NewGCM(aes) + } if err != nil { panic(err) } @@ -570,6 +587,7 @@ func (c *cthWrapper) Write(p []byte) (int, error) { return c.h.Write(p) } func (c *cthWrapper) Sum(b []byte) []byte { return c.h.ConstantTimeSum(b) } func newConstantTimeHash(h func() hash.Hash) func() hash.Hash { + boring.Unreachable() return func() hash.Hash { return &cthWrapper{h().(constantTimeHash)} } diff --git a/src/crypto/tls/common.go b/src/crypto/tls/common.go index d561e61707e..3e24d746bcf 100644 --- a/src/crypto/tls/common.go +++ b/src/crypto/tls/common.go @@ -171,11 +171,11 @@ const ( // hash function associated with the Ed25519 signature scheme. var directSigning crypto.Hash = 0 -// supportedSignatureAlgorithms contains the signature and hash algorithms that +// defaultSupportedSignatureAlgorithms contains the signature and hash algorithms that // the code advertises as supported in a TLS 1.2+ ClientHello and in a TLS 1.2+ // CertificateRequest. The two fields are merged to match with TLS 1.3. // Note that in TLS 1.2, the ECDSA algorithms are not constrained to P-256, etc. -var supportedSignatureAlgorithms = []SignatureScheme{ +var defaultSupportedSignatureAlgorithms = []SignatureScheme{ PSSWithSHA256, ECDSAWithP256AndSHA256, Ed25519, @@ -951,6 +951,9 @@ func (c *Config) time() time.Time { } func (c *Config) cipherSuites() []uint16 { + if needFIPS() { + return fipsCipherSuites(c) + } if c.CipherSuites != nil { return c.CipherSuites } @@ -967,6 +970,9 @@ var supportedVersions = []uint16{ func (c *Config) supportedVersions() []uint16 { versions := make([]uint16, 0, len(supportedVersions)) for _, v := range supportedVersions { + if needFIPS() && (v < fipsMinVersion(c) || v > fipsMaxVersion(c)) { + continue + } if c != nil && c.MinVersion != 0 && v < c.MinVersion { continue } @@ -1003,6 +1009,9 @@ func supportedVersionsFromMax(maxVersion uint16) []uint16 { var defaultCurvePreferences = []CurveID{X25519, CurveP256, CurveP384, CurveP521} func (c *Config) curvePreferences() []CurveID { + if needFIPS() { + return fipsCurvePreferences(c) + } if c == nil || len(c.CurvePreferences) == 0 { return defaultCurvePreferences } diff --git a/src/crypto/tls/fipsonly/fipsonly.go b/src/crypto/tls/fipsonly/fipsonly.go new file mode 100644 index 00000000000..85b3532d26d --- /dev/null +++ b/src/crypto/tls/fipsonly/fipsonly.go @@ -0,0 +1,27 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package fipsonly restricts all TLS configuration to FIPS-approved settings. +// +// The effect is triggered by importing the package anywhere in a program, as in: +// +// import _ "crypto/tls/fipsonly" +// +// This package only exists in the dev.boringcrypto branch of Go. +package fipsonly + +// This functionality is provided as a side effect of an import to make +// it trivial to add to an existing program. It requires only a single line +// added to an existing source file, or it can be done by adding a whole +// new source file and not modifying any existing source files. + +import ( + "crypto/internal/boring/fipstls" + "crypto/internal/boring/sig" +) + +func init() { + fipstls.Force() + sig.FIPSOnly() +} diff --git a/src/crypto/tls/fipsonly/fipsonly_test.go b/src/crypto/tls/fipsonly/fipsonly_test.go new file mode 100644 index 00000000000..facd24807dc --- /dev/null +++ b/src/crypto/tls/fipsonly/fipsonly_test.go @@ -0,0 +1,16 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package fipsonly + +import ( + "crypto/internal/boring/fipstls" + "testing" +) + +func Test(t *testing.T) { + if !fipstls.Required() { + t.Fatal("fipstls.Required() = false, must be true") + } +} diff --git a/src/crypto/tls/handshake_client.go b/src/crypto/tls/handshake_client.go index 4af3d998a36..cd1b3b1d126 100644 --- a/src/crypto/tls/handshake_client.go +++ b/src/crypto/tls/handshake_client.go @@ -117,7 +117,10 @@ func (c *Conn) makeClientHello() (*clientHelloMsg, ecdheParameters, error) { } if hello.vers >= VersionTLS12 { - hello.supportedSignatureAlgorithms = supportedSignatureAlgorithms + hello.supportedSignatureAlgorithms = supportedSignatureAlgorithms() + } + if testingOnlyForceClientHelloSignatureAlgorithms != nil { + hello.supportedSignatureAlgorithms = testingOnlyForceClientHelloSignatureAlgorithms } var params ecdheParameters @@ -856,6 +859,8 @@ func (c *Conn) verifyServerCertificate(certificates [][]byte) error { if !c.config.InsecureSkipVerify { opts := x509.VerifyOptions{ + IsBoring: isBoringCertificate, + Roots: c.config.RootCAs, CurrentTime: c.config.time(), DNSName: c.config.ServerName, diff --git a/src/crypto/tls/handshake_client_tls13.go b/src/crypto/tls/handshake_client_tls13.go index eb59ac90d11..c7989867f56 100644 --- a/src/crypto/tls/handshake_client_tls13.go +++ b/src/crypto/tls/handshake_client_tls13.go @@ -41,6 +41,10 @@ type clientHandshakeStateTLS13 struct { func (hs *clientHandshakeStateTLS13) handshake() error { c := hs.c + if needFIPS() { + return errors.New("tls: internal error: TLS 1.3 reached in FIPS mode") + } + // The server must not select TLS 1.3 in a renegotiation. See RFC 8446, // sections 4.1.2 and 4.1.3. if c.handshakes > 0 { @@ -470,7 +474,7 @@ func (hs *clientHandshakeStateTLS13) readServerCertificate() error { } // See RFC 8446, Section 4.4.3. - if !isSupportedSignatureAlgorithm(certVerify.signatureAlgorithm, supportedSignatureAlgorithms) { + if !isSupportedSignatureAlgorithm(certVerify.signatureAlgorithm, supportedSignatureAlgorithms()) { c.sendAlert(alertIllegalParameter) return errors.New("tls: certificate used with invalid signature algorithm") } diff --git a/src/crypto/tls/handshake_messages_test.go b/src/crypto/tls/handshake_messages_test.go index bb8aea86700..8821670e9ae 100644 --- a/src/crypto/tls/handshake_messages_test.go +++ b/src/crypto/tls/handshake_messages_test.go @@ -147,10 +147,10 @@ func (*clientHelloMsg) Generate(rand *rand.Rand, size int) reflect.Value { } } if rand.Intn(10) > 5 { - m.supportedSignatureAlgorithms = supportedSignatureAlgorithms + m.supportedSignatureAlgorithms = supportedSignatureAlgorithms() } if rand.Intn(10) > 5 { - m.supportedSignatureAlgorithmsCert = supportedSignatureAlgorithms + m.supportedSignatureAlgorithmsCert = supportedSignatureAlgorithms() } for i := 0; i < rand.Intn(5); i++ { m.alpnProtocols = append(m.alpnProtocols, randomString(rand.Intn(20)+1, rand)) @@ -369,10 +369,10 @@ func (*certificateRequestMsgTLS13) Generate(rand *rand.Rand, size int) reflect.V m.scts = true } if rand.Intn(10) > 5 { - m.supportedSignatureAlgorithms = supportedSignatureAlgorithms + m.supportedSignatureAlgorithms = supportedSignatureAlgorithms() } if rand.Intn(10) > 5 { - m.supportedSignatureAlgorithmsCert = supportedSignatureAlgorithms + m.supportedSignatureAlgorithmsCert = supportedSignatureAlgorithms() } if rand.Intn(10) > 5 { m.certificateAuthorities = make([][]byte, 3) diff --git a/src/crypto/tls/handshake_server.go b/src/crypto/tls/handshake_server.go index 43f30e2fefd..497e196a856 100644 --- a/src/crypto/tls/handshake_server.go +++ b/src/crypto/tls/handshake_server.go @@ -541,7 +541,7 @@ func (hs *serverHandshakeState) doFullHandshake() error { } if c.vers >= VersionTLS12 { certReq.hasSignatureAlgorithm = true - certReq.supportedSignatureAlgorithms = supportedSignatureAlgorithms + certReq.supportedSignatureAlgorithms = supportedSignatureAlgorithms() } // An empty list of certificateAuthorities signals to @@ -812,6 +812,8 @@ func (c *Conn) processCertsFromClient(certificate Certificate) error { if c.config.ClientAuth >= VerifyClientCertIfGiven && len(certs) > 0 { opts := x509.VerifyOptions{ + IsBoring: isBoringCertificate, + Roots: c.config.ClientCAs, CurrentTime: c.config.time(), Intermediates: x509.NewCertPool(), diff --git a/src/crypto/tls/handshake_server_tls13.go b/src/crypto/tls/handshake_server_tls13.go index 6aa52698a3a..1b2bf4dcd3b 100644 --- a/src/crypto/tls/handshake_server_tls13.go +++ b/src/crypto/tls/handshake_server_tls13.go @@ -45,6 +45,10 @@ type serverHandshakeStateTLS13 struct { func (hs *serverHandshakeStateTLS13) handshake() error { c := hs.c + if needFIPS() { + return errors.New("tls: internal error: TLS 1.3 reached in FIPS mode") + } + // For an overview of the TLS 1.3 handshake, see RFC 8446, Section 2. if err := hs.processClientHello(); err != nil { return err @@ -584,7 +588,7 @@ func (hs *serverHandshakeStateTLS13) sendServerCertificate() error { certReq := new(certificateRequestMsgTLS13) certReq.ocspStapling = true certReq.scts = true - certReq.supportedSignatureAlgorithms = supportedSignatureAlgorithms + certReq.supportedSignatureAlgorithms = supportedSignatureAlgorithms() if c.config.ClientCAs != nil { certReq.certificateAuthorities = c.config.ClientCAs.Subjects() } @@ -816,7 +820,7 @@ func (hs *serverHandshakeStateTLS13) readClientCertificate() error { } // See RFC 8446, Section 4.4.3. - if !isSupportedSignatureAlgorithm(certVerify.signatureAlgorithm, supportedSignatureAlgorithms) { + if !isSupportedSignatureAlgorithm(certVerify.signatureAlgorithm, supportedSignatureAlgorithms()) { c.sendAlert(alertIllegalParameter) return errors.New("tls: client certificate used with invalid signature algorithm") } |