Commit 035b9d2b authored by Filippo Valsorda's avatar Filippo Valsorda Committed by Roland Shoemaker
Browse files

crypto/tls: align FIPS-only mode with BoringSSL policy

This enables TLS 1.3, disables P-521, and disables non-ECDHE suites.

Fixes #64717
Updates #62372

Change-Id: I3a65b239ef0198bbdbe5e55e0810e7128f90a091
Reviewed-on: https://go-review.googlesource.com/c/go/+/549975


Reviewed-by: default avatarRoland Shoemaker <roland@golang.org>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: default avatarThan McIntosh <thanm@google.com>
parent 1be719a6
Loading
Loading
Loading
Loading
+15 −11
Original line number Diff line number Diff line
@@ -6,9 +6,10 @@

package tls

import (
	"crypto/internal/boring/fipstls"
)
import "crypto/internal/boring/fipstls"

// The FIPS-only policies enforced here currently match BoringSSL's
// ssl_policy_fips_202205.

// needFIPS returns fipstls.Required(); it avoids a new import in common.go.
func needFIPS() bool {
@@ -17,19 +18,19 @@ func needFIPS() bool {

// fipsMinVersion replaces c.minVersion in FIPS-only mode.
func fipsMinVersion(c *Config) uint16 {
	// FIPS requires TLS 1.2.
	// FIPS requires TLS 1.2 or TLS 1.3.
	return VersionTLS12
}

// fipsMaxVersion replaces c.maxVersion in FIPS-only mode.
func fipsMaxVersion(c *Config) uint16 {
	// FIPS requires TLS 1.2.
	return VersionTLS12
	// FIPS requires TLS 1.2 or TLS 1.3.
	return VersionTLS13
}

// default defaultFIPSCurvePreferences is the FIPS-allowed curves,
// in preference order (most preferable first).
var defaultFIPSCurvePreferences = []CurveID{CurveP256, CurveP384, CurveP521}
var defaultFIPSCurvePreferences = []CurveID{CurveP256, CurveP384}

// fipsCurvePreferences replaces c.curvePreferences in FIPS-only mode.
func fipsCurvePreferences(c *Config) []CurveID {
@@ -54,8 +55,6 @@ var defaultCipherSuitesFIPS = []uint16{
	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.
@@ -75,8 +74,14 @@ func fipsCipherSuites(c *Config) []uint16 {
	return list
}

// defaultCipherSuitesTLS13FIPS are the FIPS-allowed cipher suites for TLS 1.3.
var defaultCipherSuitesTLS13FIPS = []uint16{
	TLS_AES_128_GCM_SHA256,
	TLS_AES_256_GCM_SHA384,
}

// fipsSupportedSignatureAlgorithms currently are a subset of
// defaultSupportedSignatureAlgorithms without Ed25519 and SHA-1.
// defaultSupportedSignatureAlgorithms without Ed25519, SHA-1, and P-521.
var fipsSupportedSignatureAlgorithms = []SignatureScheme{
	PSSWithSHA256,
	PSSWithSHA384,
@@ -86,7 +91,6 @@ var fipsSupportedSignatureAlgorithms = []SignatureScheme{
	PKCS1WithSHA384,
	ECDSAWithP384AndSHA384,
	PKCS1WithSHA512,
	ECDSAWithP521AndSHA512,
}

// supportedSignatureAlgorithms returns the supported signature algorithms.
+51 −18
Original line number Diff line number Diff line
@@ -25,6 +25,31 @@ import (
	"time"
)

func allCipherSuitesIncludingTLS13() []uint16 {
	s := allCipherSuites()
	for _, suite := range cipherSuitesTLS13 {
		s = append(s, suite.id)
	}
	return s
}

func isTLS13CipherSuite(id uint16) bool {
	for _, suite := range cipherSuitesTLS13 {
		if id == suite.id {
			return true
		}
	}
	return false
}

func generateKeyShare(group CurveID) keyShare {
	key, err := generateECDHEKey(rand.Reader, group)
	if err != nil {
		panic(err)
	}
	return keyShare{group: group, data: key.PublicKey().Bytes()}
}

func TestBoringServerProtocolVersion(t *testing.T) {
	test := func(name string, v uint16, msg string) {
		t.Run(name, func(t *testing.T) {
@@ -33,8 +58,11 @@ func TestBoringServerProtocolVersion(t *testing.T) {
			clientHello := &clientHelloMsg{
				vers:               v,
				random:             make([]byte, 32),
				cipherSuites:       allCipherSuites(),
				cipherSuites:       allCipherSuitesIncludingTLS13(),
				compressionMethods: []uint8{compressionNone},
				supportedCurves:    defaultCurvePreferences,
				keyShares:          []keyShare{generateKeyShare(CurveP256)},
				supportedPoints:    []uint8{pointFormatUncompressed},
				supportedVersions:  []uint16{v},
			}
			testClientHelloFailure(t, serverConfig, clientHello, msg)
@@ -48,25 +76,25 @@ func TestBoringServerProtocolVersion(t *testing.T) {

	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")
	test("VersionSSL30/fipstls", VersionSSL30, "client offered only unsupported versions")
	test("VersionTLS10/fipstls", VersionTLS10, "client offered only unsupported versions")
	test("VersionTLS11/fipstls", VersionTLS11, "client offered only unsupported versions")
	test("VersionTLS12/fipstls", VersionTLS12, "")
	test("VersionTLS13/fipstls", VersionTLS13, "")
}

func isBoringVersion(v uint16) bool {
	return v == VersionTLS12
	return v == VersionTLS12 || v == VersionTLS13
}

func isBoringCipherSuite(id uint16) bool {
	switch id {
	case TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
	case TLS_AES_128_GCM_SHA256,
		TLS_AES_256_GCM_SHA384,
		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:
		TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384:
		return true
	}
	return false
@@ -74,7 +102,7 @@ func isBoringCipherSuite(id uint16) bool {

func isBoringCurve(id CurveID) bool {
	switch id {
	case CurveP256, CurveP384, CurveP521:
	case CurveP256, CurveP384:
		return true
	}
	return false
@@ -86,7 +114,7 @@ func isECDSA(id uint16) bool {
			return suite.flags&suiteECSign == suiteECSign
		}
	}
	panic(fmt.Sprintf("unknown cipher suite %#x", id))
	return false // TLS 1.3 cipher suites are not tied to the signature algorithm.
}

func isBoringSignatureScheme(alg SignatureScheme) bool {
@@ -98,7 +126,6 @@ func isBoringSignatureScheme(alg SignatureScheme) bool {
		PKCS1WithSHA384,
		ECDSAWithP384AndSHA384,
		PKCS1WithSHA512,
		ECDSAWithP521AndSHA512,
		PSSWithSHA256,
		PSSWithSHA384,
		PSSWithSHA512:
@@ -109,10 +136,9 @@ func isBoringSignatureScheme(alg SignatureScheme) bool {

func TestBoringServerCipherSuites(t *testing.T) {
	serverConfig := testConfig.Clone()
	serverConfig.CipherSuites = allCipherSuites()
	serverConfig.Certificates = make([]Certificate, 1)

	for _, id := range allCipherSuites() {
	for _, id := range allCipherSuitesIncludingTLS13() {
		if isECDSA(id) {
			serverConfig.Certificates[0].Certificate = [][]byte{testECDSACertificate}
			serverConfig.Certificates[0].PrivateKey = testECDSAPrivateKey
@@ -121,14 +147,19 @@ func TestBoringServerCipherSuites(t *testing.T) {
			serverConfig.Certificates[0].PrivateKey = testRSAPrivateKey
		}
		serverConfig.BuildNameToCertificate()
		t.Run(fmt.Sprintf("suite=%#x", id), func(t *testing.T) {
		t.Run(fmt.Sprintf("suite=%s", CipherSuiteName(id)), func(t *testing.T) {
			clientHello := &clientHelloMsg{
				vers:               VersionTLS12,
				random:             make([]byte, 32),
				cipherSuites:       []uint16{id},
				compressionMethods: []uint8{compressionNone},
				supportedCurves:    defaultCurvePreferences,
				keyShares:          []keyShare{generateKeyShare(CurveP256)},
				supportedPoints:    []uint8{pointFormatUncompressed},
				supportedVersions:  []uint16{VersionTLS12},
			}
			if isTLS13CipherSuite(id) {
				clientHello.supportedVersions = []uint16{VersionTLS13}
			}

			testClientHello(t, serverConfig, clientHello)
@@ -160,7 +191,9 @@ func TestBoringServerCurves(t *testing.T) {
				cipherSuites:       []uint16{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256},
				compressionMethods: []uint8{compressionNone},
				supportedCurves:    []CurveID{curveid},
				keyShares:          []keyShare{generateKeyShare(curveid)},
				supportedPoints:    []uint8{pointFormatUncompressed},
				supportedVersions:  []uint16{VersionTLS12},
			}

			testClientHello(t, serverConfig, clientHello)
@@ -279,7 +312,7 @@ func TestBoringClientHello(t *testing.T) {
	}

	if !isBoringVersion(hello.vers) {
		t.Errorf("client vers=%#x, want %#x (TLS 1.2)", hello.vers, VersionTLS12)
		t.Errorf("client vers=%#x", hello.vers)
	}
	for _, v := range hello.supportedVersions {
		if !isBoringVersion(v) {
+7 −1
Original line number Diff line number Diff line
@@ -556,7 +556,13 @@ func aeadAESGCMTLS13(key, nonceMask []byte) aead {
	if err != nil {
		panic(err)
	}
	aead, err := cipher.NewGCM(aes)
	var aead cipher.AEAD
	if boring.Enabled {
		aead, err = boring.NewGCMTLS13(aes)
	} else {
		boring.Unreachable()
		aead, err = cipher.NewGCM(aes)
	}
	if err != nil {
		panic(err)
	}
+3 −1
Original line number Diff line number Diff line
@@ -139,7 +139,9 @@ func (c *Conn) makeClientHello() (*clientHelloMsg, *ecdh.PrivateKey, error) {
		if len(hello.supportedVersions) == 1 {
			hello.cipherSuites = nil
		}
		if hasAESGCMHardwareSupport {
		if needFIPS() {
			hello.cipherSuites = append(hello.cipherSuites, defaultCipherSuitesTLS13FIPS...)
		} else if hasAESGCMHardwareSupport {
			hello.cipherSuites = append(hello.cipherSuites, defaultCipherSuitesTLS13...)
		} else {
			hello.cipherSuites = append(hello.cipherSuites, defaultCipherSuitesTLS13NoAES...)
+0 −4
Original line number Diff line number Diff line
@@ -41,10 +41,6 @@ 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 {
Loading