--- /dev/null
+// 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
+ })
+}
--- /dev/null
+// 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)
+}
import (
"crypto"
"crypto/hmac"
+ "crypto/internal/fips/tls12"
"crypto/md5"
"crypto/sha1"
"crypto/sha256"
"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]
}
// 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
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)
}
}
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)
}
}
-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
}
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
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]
buffer []byte
version uint16
- prf func(result, secret, label, seed []byte)
+ prf prfFunc
}
func (h *finishedHash) Write(msg []byte) (n int, err error) {
// 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
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
}
}