diff --git a/boring.go b/boring.go new file mode 100644 index 0000000..1827f76 --- /dev/null +++ b/boring.go @@ -0,0 +1,98 @@ +// 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. + +//go:build boringcrypto + +package tls + +import ( + "crypto/internal/boring/fipstls" +) + +// 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 +} + +// 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 +} diff --git a/boring_test.go b/boring_test.go new file mode 100644 index 0000000..ba68f35 --- /dev/null +++ b/boring_test.go @@ -0,0 +1,617 @@ +// 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. + +//go:build boringcrypto + +package tls + +import ( + "crypto/ecdsa" + "crypto/elliptic" + "crypto/internal/boring/fipstls" + "crypto/rand" + "crypto/rsa" + "crypto/x509" + "crypto/x509/pkix" + "encoding/pem" + "fmt" + "internal/obscuretestdata" + "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, clientConfig).Handshake() + srv := Server(s, testConfig) + msg, err := srv.readHandshake(nil) + 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, 512), 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) + + // 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{} + switch k := key.(type) { + case *rsa.PrivateKey: + pub = &k.PublicKey + case *ecdsa.PrivateKey: + pub = &k.PublicKey + 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) + } + + fipsOK := mode&boringCertFIPSOK != 0 + 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(obscuretestdata.Rot13([]byte(` +-----ORTVA PREGVSVPNGR----- +ZVVP/mPPNrrtNjVONtVENYUUK/xu4+4mZH9QnemORpDjQDLWXbMVuipANDRYODNj +RwRDZN4TN1HRPuZUDJAgMFOQomNrSj0kZGNkZQRkAGN0ZQInSj0lZQRlZwxkAGN0 +ZQInZOVkRQNBOtAIONbGO0SwoJHtD28jttRvZN0TPFdTFVo3QDRONDHNN4VOQjNj +ttRXNbVONDPs8sx0A6vrPOK4VBIVsXvgg4xTpBDYrvzPsfwddUplfZVITRgSFZ6R +4Nl141s/7VdqJ0HgVdAo4CKuEBVQ7lQkE284kY6KoPhi/g5uC3HpruLp3uzYvlIq +ZxMDvMJgsHHWs/1dBgZ+buAt59YEJc4q+6vK0yn1WY3RjPVpxxAwW9uDoS7Co2PF ++RF9Lb55XNnc8XBoycpE8ZOFA38odajwsDqPKiBRBwnz2UHkXmRSK5ZN+sN0zr4P +vbPpPEYJXy+TbA9S8sNOsbM+G+2rny4QYhB95eKE8FeBVIOu3KSBe/EIuwgKpAIS +MXpiQg6q68I6wNXNLXz5ayw9TCcq4i+eNtZONNTwHQOBZN4TN1HqQjRO/jDRNjVS +bQNGOtAIUFHRQQNXOtteOtRSODpQNGNZOtAIUEZONs8RNwNNZOxTN1HqRDDFZOPP +QzI4LJ1joTHhM29fLJ5aZN0TPFdTFVo3QDROPjHNN4VONDPBbLfIpSPOuobdr3JU +qP6I7KKKRPzawu01e8u80li0AE379aFQ3pj2Z+UXinKlfJdey5uwTIXj0igjQ81e +I4WmQh7VsVbt5z8+DAP+7YdQMfm88iQXBefblFIBzHPtzPXSKrj+YN+rB/vDRWGe +7rafqqBrKWRc27Rq5iJ+xzJJ3Dztyp2Tjl8jSeZQVdaeaBmON4bPaQRtgKWg0mbt +aEjosRZNJv1nDEl5qG9XN3FC9zb5FrGSFmTTUvR4f4tUHr7wifNSS2dtgQ6+jU6f +m9o6fukaP7t5VyOXuV7FIO/Hdg2lqW+xU1LowZpVd6ANZ5rAZXtMhWe3+mjfFtju +TAnR +-----RAQ PREGVSVPNGR-----`))) + testRSA2048Certificate = block.Bytes + + block, _ = pem.Decode(obscuretestdata.Rot13([]byte(` +-----ORTVA EFN CEVINGR XRL----- +ZVVRcNVONNXPNDRNa/U5AQrbattI+PQyFUlbeorWOaQxP3bcta7V6du3ZeQPSEuY +EHwBuBNZgrAK/+lXaIgSYFXwJ+Q14HGvN+8t8HqiBZF+y2jee/7rLG91UUbJUA4M +v4fyKGWTHVzIeK1SPK/9nweGCdVGLBsF0IdrUshby9WJgFF9kZNvUWWQLlsLHTkr +m29txiuRiJXBrFtTdsPwz5nKRsQNHwq/T6c8V30UDy7muQb2cgu1ZFfkOI+GNCaj +AWahNbdNaNxF1vcsudQsEsUjNK6Tsx/gazcrNl7wirn10sRdmvSDLq1kGd/0ILL7 +I3QIEJFaYj7rariSrbjPtTPchM5L/Ew6KrY/djVQNDNONbVONDPAcZMvsq/it42u +UqPiYhMnLF0E7FhaSycbKRfygTqYSfac0VsbWM/htSDOFNVVsYjZhzH6bKN1m7Hi +98nVLI61QrCeGPQIQSOfUoAzC8WNb8JgohfRojq5mlbO7YLT2+pyxWxyJR73XdHd +ezV+HWrlFpy2Tva7MGkOKm1JCOx9IjpajxrnKctNFVOJ23suRPZ9taLRRjnOrm5G +6Zr8q1gUgLDi7ifXr7eb9j9/UXeEKrwdLXX1YkxusSevlI+z8YMWMa2aKBn6T3tS +Ao8Dx1Hx5CHORAOzlZSWuG4Z/hhFd4LgZeeB2tv8D+sCuhTmp5FfuLXEOc0J4C5e +zgIPgRSENbTONZRAOVSYeI2+UfTw0kLSnfXbi/DCr6UFGE1Uu2VMBAc+bX4bfmJR +wOG4IpaVGzcy6gP1Jl4TpekwAtXVSMNw+1k1YHHYqbeKxhT8le0gNuT9mAlsJfFl +CeFbiP0HIome8Wkkyn+xDIkRDDdJDkCyRIhY8xKnVQN6Ylg1Uchn2YiCNbTONADM +p6Yd2G7+OkYkAqv2z8xMmrw5xtmOc/KqIfoSJEyroVK2XeSUfeUmG9CHx3QR1iMX +Z6cmGg94aDuJFxQtPnj1FbuRyW3USVSjphfS1FWNp3cDrcq8ht6VLqycQZYgOw/C +/5C6OIHgtb05R4+V/G3vLngztyDkGgyM0ExFI2yyNbTONYBKxXSK7nuCis0JxfQu +hGshSBGCbbjtDT0RctJ0jEqPkrt/WYvp3yFQ0tfggDI2JfErpelJpknryEt10EzB +38OobtzunS4kitfFihwBsvMGR8bX1G43Z+6AXfVyZY3LVYocH/9nWkCJl0f2QdQe +pDWuMeyx+cmwON7Oas/HEqjkNbTNXE/PAj14Q+zeY3LYoovPKvlqdkIjki5cqMqm +8guv3GApfJP4vTHEqpIdosHvaICqWvKr/Xnp3JTPrEWnSItoXNBkYgv1EO5ZxVut +Q8rlhcOdx4J1Y1txekdfqw4GSykxjZljwy2R2F4LlD8COg6I04QbIEMfVXmdm+CS +HvbaCd0PtLOPLKidvbWuCrjxBd/L5jeQOrMJ1SDX5DQ9J5Z8/5mkq4eqiWgwuoWc +bBegiZqey6hcl9Um4OWQ3SKjISvCSR7wdrAdv0S21ivYkOCZZQ3HBQS6YY5RlYvE +9I4kIZF8XKkit7ekfhdmZCfpIvnJHY6JAIOufQ2+92qUkFKmm5RWXD== +-----RAQ EFN CEVINGR XRL-----`))) + var err error + testRSA2048PrivateKey, err = x509.ParsePKCS1PrivateKey(block.Bytes) + if err != nil { + panic(err) + } +} diff --git a/conn.go b/conn.go index 28424e7..b805331 100644 --- a/conn.go +++ b/conn.go @@ -96,9 +96,7 @@ type Conn struct { // clientProtocol is the negotiated ALPN protocol. clientProtocol string - // [UTLS SECTION START] - utls utlsConnExtraFields // used for extensive things such as ALPS - // [UTLS SECTION END] + utls utlsConnExtraFields // [UTLS】 used for extensive things such as ALPS // input/output in, out halfConn diff --git a/handshake_client.go b/handshake_client.go index 8116d8d..eae1a42 100644 --- a/handshake_client.go +++ b/handshake_client.go @@ -252,15 +252,16 @@ func (c *Conn) clientHandshake(ctx context.Context) (err error) { if c.vers == VersionTLS13 { hs := &clientHandshakeStateTLS13{ - c: c, - ctx: ctx, - serverHello: serverHello, - hello: hello, - ecdheKey: ecdheKey, + c: c, + ctx: ctx, + serverHello: serverHello, + hello: hello, + ecdheKey: ecdheKey, + session: session, + earlySecret: earlySecret, + binderKey: binderKey, + keySharesEcdheParams: make(KeySharesEcdheParameters, 2), // [uTLS] - session: session, - earlySecret: earlySecret, - binderKey: binderKey, } // In TLS 1.3, session tickets are delivered after the handshake. @@ -960,6 +961,10 @@ func (hs *clientHandshakeState) sendFinished(out []byte) error { return nil } +// maxRSAKeySize is the maximum RSA key size in bits that we are willing +// to verify the signatures of during a TLS handshake. +const maxRSAKeySize = 8192 + // verifyServerCertificate parses and verifies the provided chain, setting // c.verifiedChains and c.peerCertificates or sending the appropriate alert. func (c *Conn) verifyServerCertificate(certificates [][]byte) error { @@ -971,6 +976,10 @@ func (c *Conn) verifyServerCertificate(certificates [][]byte) error { c.sendAlert(alertBadCertificate) return errors.New("tls: failed to parse certificate from server: " + err.Error()) } + if cert.cert.PublicKeyAlgorithm == x509.RSA && cert.cert.PublicKey.(*rsa.PublicKey).N.BitLen() > maxRSAKeySize { + c.sendAlert(alertBadCertificate) + return fmt.Errorf("tls: server sent certificate containing RSA key larger than %d bits", maxRSAKeySize) + } activeHandles[i] = cert certs[i] = cert.cert } @@ -1127,7 +1136,6 @@ func (c *Conn) clientSessionCacheKey() string { return "" } -// [UTLS SECTION START] // hostnameInSNI converts name into an appropriate hostname for SNI. // Literal IP addresses and absolute FQDNs are not permitted as SNI values. // See RFC 6066, Section 3. diff --git a/handshake_client_test.go b/handshake_client_test.go index 08c0af6..ee9e79a 100644 --- a/handshake_client_test.go +++ b/handshake_client_test.go @@ -2721,3 +2721,81 @@ func testTLS13OnlyClientHelloCipherSuite(t *testing.T, ciphers []uint16) { t.Fatalf("handshake failed: %s", err) } } + +// discardConn wraps a net.Conn but discards all writes, but reports that they happened. +type discardConn struct { + net.Conn +} + +func (dc *discardConn) Write(data []byte) (int, error) { + return len(data), nil +} + +// largeRSAKeyCertPEM contains a 8193 bit RSA key +const largeRSAKeyCertPEM = `-----BEGIN CERTIFICATE----- +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 +u58= +-----END CERTIFICATE-----` + +func TestHandshakeRSATooBig(t *testing.T) { + testCert, _ := pem.Decode([]byte(largeRSAKeyCertPEM)) + + c := &Conn{conn: &discardConn{}, config: testConfig.Clone()} + + expectedErr := "tls: server sent certificate containing RSA key larger than 8192 bits" + err := c.verifyServerCertificate([][]byte{testCert.Bytes}) + if err == nil || err.Error() != expectedErr { + t.Errorf("Conn.verifyServerCertificate unexpected error: want %q, got %q", expectedErr, err) + } + + expectedErr = "tls: client sent certificate containing RSA key larger than 8192 bits" + err = c.processCertsFromClient(Certificate{Certificate: [][]byte{testCert.Bytes}}) + if err == nil || err.Error() != expectedErr { + t.Errorf("Conn.processCertsFromClient unexpected error: want %q, got %q", expectedErr, err) + } +} diff --git a/handshake_client_tls13.go b/handshake_client_tls13.go index b856313..7e307c1 100644 --- a/handshake_client_tls13.go +++ b/handshake_client_tls13.go @@ -77,8 +77,8 @@ func (hs *clientHandshakeStateTLS13) handshake() error { // [uTLS SECTION START] // set echdheParams to what we received from server - if ecdheParams, ok := hs.keySharesEcdheParams.GetEcdheParams(hs.serverHello.serverShare.group); ok { - hs.ecdheKey = ecdheParams + if ecdheKey, ok := hs.keySharesEcdheParams.GetEcdheParams(hs.serverHello.serverShare.group); ok { + hs.ecdheKey = ecdheKey } // [uTLS SECTION END] diff --git a/handshake_messages.go b/handshake_messages.go index ad6db2f..0c7ba94 100644 --- a/handshake_messages.go +++ b/handshake_messages.go @@ -87,7 +87,7 @@ type clientHelloMsg struct { extendedMasterSecret bool alpnProtocols []string scts bool - // ems bool // [uTLS] actually implemented due to its prevalence + // ems bool // [uTLS] actually implemented due to its prevalence // removed since crypto/tls implements it supportedVersions []uint16 cookie []byte keyShares []keyShare @@ -642,13 +642,12 @@ type serverHelloMsg struct { secureRenegotiation []byte extendedMasterSecret bool alpnProtocol string - // ems bool - scts [][]byte - supportedVersion uint16 - serverShare keyShare - selectedIdentityPresent bool - selectedIdentity uint16 - supportedPoints []uint8 + scts [][]byte + supportedVersion uint16 + serverShare keyShare + selectedIdentityPresent bool + selectedIdentity uint16 + supportedPoints []uint8 // HelloRetryRequest extensions cookie []byte diff --git a/handshake_server.go b/handshake_server.go index ed40014..dcbda21 100644 --- a/handshake_server.go +++ b/handshake_server.go @@ -868,6 +868,10 @@ func (c *Conn) processCertsFromClient(certificate Certificate) error { c.sendAlert(alertBadCertificate) return errors.New("tls: failed to parse client certificate: " + err.Error()) } + if certs[i].PublicKeyAlgorithm == x509.RSA && certs[i].PublicKey.(*rsa.PublicKey).N.BitLen() > maxRSAKeySize { + c.sendAlert(alertBadCertificate) + return fmt.Errorf("tls: client sent certificate containing RSA key larger than %d bits", maxRSAKeySize) + } } if len(certs) == 0 && requiresClientCert(c.config.ClientAuth) { diff --git a/quic.go b/quic.go index 33ece1a..6cb10df 100644 --- a/quic.go +++ b/quic.go @@ -363,7 +363,6 @@ func (c *Conn) quicGetTransportParameters() ([]byte, error) { return nil, err } } - return c.quic.transportParams, nil } diff --git a/tls_test.go b/tls_test.go index 4be05b5..f4e282f 100644 --- a/tls_test.go +++ b/tls_test.go @@ -870,7 +870,7 @@ func TestCloneNonFuncFields(t *testing.T) { f.Set(reflect.ValueOf(RenegotiateOnceAsClient)) case "mutex", "autoSessionTicketKeys", "sessionTicketKeys": continue // these are unexported fields that are handled separately - case "ApplicationSettings": + case "ApplicationSettings": // [UTLS] ALPS (Application Settings) f.Set(reflect.ValueOf(map[string][]byte{"a": {1}})) default: t.Errorf("all fields must be accounted for, but saw unknown field %q", fn)