diff --git a/eng/doc/fips/UserGuide.md b/eng/doc/fips/UserGuide.md index 1d8f686c4e1..4f538c7e285 100644 --- a/eng/doc/fips/UserGuide.md +++ b/eng/doc/fips/UserGuide.md @@ -53,12 +53,12 @@ The Go crypto documentation is available online at https://pkg.go.dev/crypto. - [crypto/sha512](#cryptosha512) - [func New](#func-new-3) - [func New384](#func-new384) - - [func New512_224](#func-new512_224) - - [func New512_256](#func-new512_256) + - [func New512\_224](#func-new512_224) + - [func New512\_256](#func-new512_256) - [func Sum384](#func-sum384) - [func Sum512](#func-sum512) - - [func Sum512_224](#func-sum512_224) - - [func Sum512_256](#func-sum512_256) + - [func Sum512\_224](#func-sum512_224) + - [func Sum512\_256](#func-sum512_256) - [crypto/rsa](#cryptorsa) - [func DecryptOAEP](#func-decryptoaep) - [func DecryptPKCS1v15](#func-decryptpkcs1v15) @@ -1235,18 +1235,22 @@ Package tls will automatically use FIPS compliant primitives implemented in othe When using TLS in FIPS-only mode the TLS handshake has the following restrictions: -- TLS versions: `tls.VersionTLS12` +- TLS versions: + - `tls.VersionTLS12` + - `tls.VersionTLS13` - ECDSA elliptic curves: - `tls.CurveP256` - `tls.CurveP384` - `tls.CurveP521` - Cipher suites: - - `tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256` - - `tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384` - - `tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256` - - `tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384` - - `tls.TLS_RSA_WITH_AES_128_GCM_SHA256` - - `tls.TLS_RSA_WITH_AES_256_GCM_SHA384` + - TLS 1.2: + - `tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256` + - `tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384` + - `tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256` + - `tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384` + - TLS 1.3: + - `tls.TLS_AES_128_GCM_SHA256` + - `tls.TLS_AES_256_GCM_SHA384` - x509 certificate public key: - `rsa.PublicKey` with a bit length of 2048 or 3072. Bit length of 4096 is still not supported, see [this issue](https://github.com/golang/go/issues/41147) for more info. - `ecdsa.PublicKey` with a supported elliptic curve. diff --git a/patches/0004-Add-BoringSSL-crypto-backend.patch b/patches/0004-Add-BoringSSL-crypto-backend.patch index 5d7c6e3cd8c..921fa512c72 100644 --- a/patches/0004-Add-BoringSSL-crypto-backend.patch +++ b/patches/0004-Add-BoringSSL-crypto-backend.patch @@ -72,7 +72,7 @@ index 00000000000000..ba4f290a8e394d + +func NewAESCipher(key []byte) (cipher.Block, error) { return boring.NewAESCipher(key) } +func NewGCMTLS(c cipher.Block) (cipher.AEAD, error) { return boring.NewGCMTLS(c) } -+func NewGCMTLS13(c cipher.Block) (cipher.AEAD, error) { return boring.NewGCMTLS13(c) } ++func NewGCMTLS13(c cipher.Block) (cipher.AEAD, error) { panic("cryptobackend: not available") } + +type PublicKeyECDSA = boring.PublicKeyECDSA +type PrivateKeyECDSA = boring.PrivateKeyECDSA diff --git a/patches/0008-Support-TLS-1.3-in-fipstls-mode.patch b/patches/0008-Support-TLS-1.3-in-fipstls-mode.patch new file mode 100644 index 00000000000..da583e1724f --- /dev/null +++ b/patches/0008-Support-TLS-1.3-in-fipstls-mode.patch @@ -0,0 +1,325 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: qmuntal +Date: Fri, 2 Feb 2024 09:25:38 +0100 +Subject: [PATCH] Support TLS 1.3 in fipstls mode + +--- + src/crypto/tls/boring.go | 12 +++-- + src/crypto/tls/boring_test.go | 56 +++++++++++++++++++----- + src/crypto/tls/cipher_suites.go | 13 +++++- + src/crypto/tls/handshake_client.go | 4 +- + src/crypto/tls/handshake_client_tls13.go | 4 -- + src/crypto/tls/handshake_server_test.go | 28 ++++++++---- + src/crypto/tls/handshake_server_tls13.go | 7 ++- + src/crypto/tls/notboring.go | 2 + + 8 files changed, 93 insertions(+), 33 deletions(-) + +diff --git a/src/crypto/tls/boring.go b/src/crypto/tls/boring.go +index ecd0f5a7b3e9ed..35808a3acd5403 100644 +--- a/src/crypto/tls/boring.go ++++ b/src/crypto/tls/boring.go +@@ -17,14 +17,14 @@ 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, +@@ -75,6 +75,12 @@ 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. + var fipsSupportedSignatureAlgorithms = []SignatureScheme{ +diff --git a/src/crypto/tls/boring_test.go b/src/crypto/tls/boring_test.go +index 3e63ba6a053c42..e278f8ad1a8118 100644 +--- a/src/crypto/tls/boring_test.go ++++ b/src/crypto/tls/boring_test.go +@@ -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,15 +76,15 @@ 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 { +@@ -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 { +@@ -109,10 +137,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 +@@ -128,7 +155,12 @@ func TestBoringServerCipherSuites(t *testing.T) { + 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 +192,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 +313,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) { +diff --git a/src/crypto/tls/cipher_suites.go b/src/crypto/tls/cipher_suites.go +index 68598961fda6e4..8c86b8ad998559 100644 +--- a/src/crypto/tls/cipher_suites.go ++++ b/src/crypto/tls/cipher_suites.go +@@ -17,6 +17,7 @@ import ( + "fmt" + "hash" + "internal/cpu" ++ "internal/goexperiment" + "runtime" + + "golang.org/x/crypto/chacha20poly1305" +@@ -540,7 +541,17 @@ func aeadAESGCMTLS13(key, nonceMask []byte) aead { + if err != nil { + panic(err) + } +- aead, err := cipher.NewGCM(aes) ++ var aead cipher.AEAD ++ if boring.Enabled { ++ if goexperiment.BoringCrypto { ++ aead, err = cipher.NewGCM(aes) ++ } else { ++ aead, err = boring.NewGCMTLS13(aes) ++ } ++ } else { ++ boring.Unreachable() ++ aead, err = cipher.NewGCM(aes) ++ } + if err != nil { + panic(err) + } +diff --git a/src/crypto/tls/handshake_client.go b/src/crypto/tls/handshake_client.go +index 0bb6a0aaef2fff..aee451ad7aa10f 100644 +--- a/src/crypto/tls/handshake_client.go ++++ b/src/crypto/tls/handshake_client.go +@@ -129,7 +129,9 @@ func (c *Conn) makeClientHello() (*clientHelloMsg, *ecdh.PrivateKey, error) { + + var key *ecdh.PrivateKey + if hello.supportedVersions[0] == VersionTLS13 { +- 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...) +diff --git a/src/crypto/tls/handshake_client_tls13.go b/src/crypto/tls/handshake_client_tls13.go +index 4a8661085ebf57..87fe11de5c5be4 100644 +--- a/src/crypto/tls/handshake_client_tls13.go ++++ b/src/crypto/tls/handshake_client_tls13.go +@@ -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 { +diff --git a/src/crypto/tls/handshake_server_test.go b/src/crypto/tls/handshake_server_test.go +index 04abdcca89040d..778972d38dbcf1 100644 +--- a/src/crypto/tls/handshake_server_test.go ++++ b/src/crypto/tls/handshake_server_test.go +@@ -27,6 +27,7 @@ import ( + ) + + func testClientHello(t *testing.T, serverConfig *Config, m handshakeMessage) { ++ t.Helper() + testClientHelloFailure(t, serverConfig, m, "") + } + +@@ -52,23 +53,32 @@ func testClientHelloFailure(t *testing.T, serverConfig *Config, m handshakeMessa + ctx := context.Background() + conn := Server(s, serverConfig) + ch, err := conn.readClientHello(ctx) +- hs := serverHandshakeState{ +- c: conn, +- ctx: ctx, +- clientHello: ch, +- } +- if err == nil { ++ if err == nil && conn.vers == VersionTLS13 { ++ hs := serverHandshakeStateTLS13{ ++ c: conn, ++ ctx: ctx, ++ clientHello: ch, ++ } + err = hs.processClientHello() +- } +- if err == nil { +- err = hs.pickCipherSuite() ++ } else if err == nil { ++ hs := serverHandshakeState{ ++ c: conn, ++ ctx: ctx, ++ clientHello: ch, ++ } ++ err = hs.processClientHello() ++ if err == nil { ++ err = hs.pickCipherSuite() ++ } + } + s.Close() + if len(expectedSubStr) == 0 { + if err != nil && err != io.EOF { ++ t.Helper() + t.Errorf("Got error: %s; expected to succeed", err) + } + } else if err == nil || !strings.Contains(err.Error(), expectedSubStr) { ++ t.Helper() + t.Errorf("Got error: %v; expected to match substring '%s'", err, expectedSubStr) + } + } +diff --git a/src/crypto/tls/handshake_server_tls13.go b/src/crypto/tls/handshake_server_tls13.go +index d1c5780533bf6a..2980ff58e65d31 100644 +--- a/src/crypto/tls/handshake_server_tls13.go ++++ b/src/crypto/tls/handshake_server_tls13.go +@@ -45,10 +45,6 @@ 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 +@@ -158,6 +154,9 @@ func (hs *serverHandshakeStateTLS13) processClientHello() error { + if !hasAESGCMHardwareSupport || !aesgcmPreferred(hs.clientHello.cipherSuites) { + preferenceList = defaultCipherSuitesTLS13NoAES + } ++ if needFIPS() { ++ preferenceList = defaultCipherSuitesTLS13FIPS ++ } + for _, suiteID := range preferenceList { + hs.suite = mutualCipherSuiteTLS13(hs.clientHello.cipherSuites, suiteID) + if hs.suite != nil { +diff --git a/src/crypto/tls/notboring.go b/src/crypto/tls/notboring.go +index 5a133c9b2f94c7..7625ccb867dd92 100644 +--- a/src/crypto/tls/notboring.go ++++ b/src/crypto/tls/notboring.go +@@ -18,3 +18,5 @@ func fipsCurvePreferences(c *Config) []CurveID { panic("fipsCurvePreferences") } + func fipsCipherSuites(c *Config) []uint16 { panic("fipsCipherSuites") } + + var fipsSupportedSignatureAlgorithms []SignatureScheme ++ ++var defaultCipherSuitesTLS13FIPS []uint16