diff --git a/src/crypto/internal/fips/cast_external_test.go b/src/crypto/internal/fips/cast_external_test.go index 1e4465dcee9..d31086d5e33 100644 --- a/src/crypto/internal/fips/cast_external_test.go +++ b/src/crypto/internal/fips/cast_external_test.go @@ -18,6 +18,7 @@ import ( _ "crypto/internal/fips/sha256" _ "crypto/internal/fips/sha3" _ "crypto/internal/fips/sha512" + _ "crypto/internal/fips/tls12" _ "crypto/internal/fips/tls13" ) diff --git a/src/crypto/internal/fips/tls12/cast.go b/src/crypto/internal/fips/tls12/cast.go new file mode 100644 index 00000000000..9c48947ab1f --- /dev/null +++ b/src/crypto/internal/fips/tls12/cast.go @@ -0,0 +1,37 @@ +// Copyright 2024 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 tls12 + +import ( + "bytes" + "crypto/internal/fips" + "crypto/internal/fips/sha256" + "errors" +) + +func init() { + fips.CAST("TLSv1.2-SHA2-256", func() error { + input := []byte{ + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, + } + transcript := []byte{ + 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, + 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, + } + want := []byte{ + 0x8c, 0x3e, 0xed, 0xa7, 0x1c, 0x1b, 0x4c, 0xc0, + 0xa0, 0x44, 0x90, 0x75, 0xa8, 0x8e, 0xbc, 0x7c, + 0x5e, 0x1c, 0x4b, 0x1e, 0x4f, 0xe3, 0xc1, 0x06, + 0xeb, 0xdc, 0xc0, 0x5d, 0xc0, 0xc8, 0xec, 0xf3, + 0xe2, 0xb9, 0xd1, 0x03, 0x5e, 0xb2, 0x60, 0x5d, + 0x12, 0x68, 0x4f, 0x49, 0xdf, 0xa9, 0x9d, 0xcc, + } + if got := MasterSecret(sha256.New, input, transcript); !bytes.Equal(got, want) { + return errors.New("unexpected result") + } + return nil + }) +} diff --git a/src/crypto/internal/fips/tls12/tls12.go b/src/crypto/internal/fips/tls12/tls12.go new file mode 100644 index 00000000000..0a70e9d963f --- /dev/null +++ b/src/crypto/internal/fips/tls12/tls12.go @@ -0,0 +1,69 @@ +// Copyright 2024 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 tls12 + +import ( + "crypto/internal/fips" + "crypto/internal/fips/hmac" + "crypto/internal/fips/sha256" + "crypto/internal/fips/sha512" +) + +// PRF implements the TLS 1.2 pseudo-random function, as defined in RFC 5246, +// Section 5 and allowed by SP 800-135, Revision 1, Section 4.2.2. +func PRF[H fips.Hash](hash func() H, secret []byte, label string, seed []byte, keyLen int) []byte { + labelAndSeed := make([]byte, len(label)+len(seed)) + copy(labelAndSeed, label) + copy(labelAndSeed[len(label):], seed) + + result := make([]byte, keyLen) + pHash(hash, result, secret, labelAndSeed) + return result +} + +// pHash implements the P_hash function, as defined in RFC 5246, Section 5. +func pHash[H fips.Hash](hash func() H, result, secret, seed []byte) { + h := hmac.New(hash, secret) + h.Write(seed) + a := h.Sum(nil) + + for len(result) > 0 { + h.Reset() + h.Write(a) + h.Write(seed) + b := h.Sum(nil) + n := copy(result, b) + result = result[n:] + + h.Reset() + h.Write(a) + a = h.Sum(nil) + } +} + +const masterSecretLength = 48 +const extendedMasterSecretLabel = "extended master secret" + +// MasterSecret implements the TLS 1.2 extended master secret derivation, as +// defined in RFC 7627 and allowed by SP 800-135, Revision 1, Section 4.2.2. +func MasterSecret[H fips.Hash](hash func() H, preMasterSecret, transcript []byte) []byte { + // "The TLS 1.2 KDF is an approved KDF when the following conditions are + // satisfied: [...] (3) P_HASH uses either SHA-256, SHA-384 or SHA-512." + h := hash() + switch any(h).(type) { + case *sha256.Digest: + if h.Size() != 32 { + fips.RecordNonApproved() + } + case *sha512.Digest: + if h.Size() != 46 && h.Size() != 64 { + fips.RecordNonApproved() + } + default: + fips.RecordNonApproved() + } + + return PRF(hash, preMasterSecret, extendedMasterSecretLabel, transcript, masterSecretLength) +} diff --git a/src/crypto/tls/prf.go b/src/crypto/tls/prf.go index a7fa3370e66..c306ca40e65 100644 --- a/src/crypto/tls/prf.go +++ b/src/crypto/tls/prf.go @@ -7,6 +7,7 @@ package tls import ( "crypto" "crypto/hmac" + "crypto/internal/fips/tls12" "crypto/md5" "crypto/sha1" "crypto/sha256" @@ -16,6 +17,8 @@ import ( "hash" ) +type prfFunc func(secret []byte, label string, seed []byte, keyLen int) []byte + // Split a premaster secret in two as specified in RFC 4346, Section 5. func splitPreMasterSecret(secret []byte) (s1, s2 []byte) { s1 = secret[0 : (len(secret)+1)/2] @@ -45,7 +48,8 @@ func pHash(result, secret, seed []byte, hash func() hash.Hash) { } // prf10 implements the TLS 1.0 pseudo-random function, as defined in RFC 2246, Section 5. -func prf10(result, secret, label, seed []byte) { +func prf10(secret []byte, label string, seed []byte, keyLen int) []byte { + result := make([]byte, keyLen) hashSHA1 := sha1.New hashMD5 := md5.New @@ -61,16 +65,14 @@ func prf10(result, secret, label, seed []byte) { for i, b := range result2 { result[i] ^= b } + + return result } // prf12 implements the TLS 1.2 pseudo-random function, as defined in RFC 5246, Section 5. -func prf12(hashFunc func() hash.Hash) func(result, secret, label, seed []byte) { - return func(result, secret, label, seed []byte) { - labelAndSeed := make([]byte, len(label)+len(seed)) - copy(labelAndSeed, label) - copy(labelAndSeed[len(label):], seed) - - pHash(result, secret, labelAndSeed, hashFunc) +func prf12(hashFunc func() hash.Hash) prfFunc { + return func(secret []byte, label string, seed []byte, keyLen int) []byte { + return tls12.PRF(hashFunc, secret, label, seed, keyLen) } } @@ -79,13 +81,13 @@ const ( finishedVerifyLength = 12 // Length of verify_data in a Finished message. ) -var masterSecretLabel = []byte("master secret") -var extendedMasterSecretLabel = []byte("extended master secret") -var keyExpansionLabel = []byte("key expansion") -var clientFinishedLabel = []byte("client finished") -var serverFinishedLabel = []byte("server finished") +const masterSecretLabel = "master secret" +const extendedMasterSecretLabel = "extended master secret" +const keyExpansionLabel = "key expansion" +const clientFinishedLabel = "client finished" +const serverFinishedLabel = "server finished" -func prfAndHashForVersion(version uint16, suite *cipherSuite) (func(result, secret, label, seed []byte), crypto.Hash) { +func prfAndHashForVersion(version uint16, suite *cipherSuite) (prfFunc, crypto.Hash) { switch version { case VersionTLS10, VersionTLS11: return prf10, crypto.Hash(0) @@ -99,7 +101,7 @@ func prfAndHashForVersion(version uint16, suite *cipherSuite) (func(result, secr } } -func prfForVersion(version uint16, suite *cipherSuite) func(result, secret, label, seed []byte) { +func prfForVersion(version uint16, suite *cipherSuite) prfFunc { prf, _ := prfAndHashForVersion(version, suite) return prf } @@ -111,17 +113,19 @@ func masterFromPreMasterSecret(version uint16, suite *cipherSuite, preMasterSecr seed = append(seed, clientRandom...) seed = append(seed, serverRandom...) - masterSecret := make([]byte, masterSecretLength) - prfForVersion(version, suite)(masterSecret, preMasterSecret, masterSecretLabel, seed) - return masterSecret + return prfForVersion(version, suite)(preMasterSecret, masterSecretLabel, seed, masterSecretLength) } // extMasterFromPreMasterSecret generates the extended master secret from the // pre-master secret. See RFC 7627. func extMasterFromPreMasterSecret(version uint16, suite *cipherSuite, preMasterSecret, transcript []byte) []byte { - masterSecret := make([]byte, masterSecretLength) - prfForVersion(version, suite)(masterSecret, preMasterSecret, extendedMasterSecretLabel, transcript) - return masterSecret + prf, hash := prfAndHashForVersion(version, suite) + if version == VersionTLS12 { + // Use the FIPS 140-3 module only for TLS 1.2 with EMS, which is the + // only TLS 1.0-1.2 approved mode per IG D.Q. + return tls12.MasterSecret(hash.New, preMasterSecret, transcript) + } + return prf(preMasterSecret, extendedMasterSecretLabel, transcript, masterSecretLength) } // keysFromMasterSecret generates the connection keys from the master @@ -133,8 +137,7 @@ func keysFromMasterSecret(version uint16, suite *cipherSuite, masterSecret, clie seed = append(seed, clientRandom...) n := 2*macLen + 2*keyLen + 2*ivLen - keyMaterial := make([]byte, n) - prfForVersion(version, suite)(keyMaterial, masterSecret, keyExpansionLabel, seed) + keyMaterial := prfForVersion(version, suite)(masterSecret, keyExpansionLabel, seed, n) clientMAC = keyMaterial[:macLen] keyMaterial = keyMaterial[macLen:] serverMAC = keyMaterial[:macLen] @@ -177,7 +180,7 @@ type finishedHash struct { buffer []byte version uint16 - prf func(result, secret, label, seed []byte) + prf prfFunc } func (h *finishedHash) Write(msg []byte) (n int, err error) { @@ -209,17 +212,13 @@ func (h finishedHash) Sum() []byte { // clientSum returns the contents of the verify_data member of a client's // Finished message. func (h finishedHash) clientSum(masterSecret []byte) []byte { - out := make([]byte, finishedVerifyLength) - h.prf(out, masterSecret, clientFinishedLabel, h.Sum()) - return out + return h.prf(masterSecret, clientFinishedLabel, h.Sum(), finishedVerifyLength) } // serverSum returns the contents of the verify_data member of a server's // Finished message. func (h finishedHash) serverSum(masterSecret []byte) []byte { - out := make([]byte, finishedVerifyLength) - h.prf(out, masterSecret, serverFinishedLabel, h.Sum()) - return out + return h.prf(masterSecret, serverFinishedLabel, h.Sum(), finishedVerifyLength) } // hashForClientCertificate returns the handshake messages so far, pre-hashed if @@ -292,8 +291,6 @@ func ekmFromMasterSecret(version uint16, suite *cipherSuite, masterSecret, clien seed = append(seed, context...) } - keyMaterial := make([]byte, length) - prfForVersion(version, suite)(keyMaterial, masterSecret, []byte(label), seed) - return keyMaterial, nil + return prfForVersion(version, suite)(masterSecret, label, seed, length), nil } } diff --git a/src/go/build/deps_test.go b/src/go/build/deps_test.go index 7dc1566df7e..15e17cd4bac 100644 --- a/src/go/build/deps_test.go +++ b/src/go/build/deps_test.go @@ -462,6 +462,7 @@ var depsRules = ` < crypto/internal/fips/hmac < crypto/internal/fips/check < crypto/internal/fips/hkdf + < crypto/internal/fips/tls12 < crypto/internal/fips/tls13 < FIPS;