]> Cypherpunks repositories - gostls13.git/commitdiff
crypto/internal/fips/tls12: implement TLS 1.2 KDF
authorFilippo Valsorda <filippo@golang.org>
Sat, 9 Nov 2024 16:21:42 +0000 (17:21 +0100)
committerGopher Robot <gobot@golang.org>
Tue, 19 Nov 2024 17:45:22 +0000 (17:45 +0000)
For #69536

Change-Id: If2477c5249a7c7db45c1af05e715ae0b61e7d940
Reviewed-on: https://go-review.googlesource.com/c/go/+/626837
Reviewed-by: Roland Shoemaker <roland@golang.org>
Reviewed-by: Daniel McCarney <daniel@binaryparadox.net>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Auto-Submit: Filippo Valsorda <filippo@golang.org>
Reviewed-by: Michael Knyszek <mknyszek@google.com>
Reviewed-by: Russ Cox <rsc@golang.org>
src/crypto/internal/fips/cast_external_test.go
src/crypto/internal/fips/tls12/cast.go [new file with mode: 0644]
src/crypto/internal/fips/tls12/tls12.go [new file with mode: 0644]
src/crypto/tls/prf.go
src/go/build/deps_test.go

index 1e4465dcee99010d6fabc7595fdbd1d47d375928..d31086d5e336adb8d5b5728ea485ec3eb673cbe5 100644 (file)
@@ -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 (file)
index 0000000..9c48947
--- /dev/null
@@ -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 (file)
index 0000000..0a70e9d
--- /dev/null
@@ -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)
+}
index a7fa3370e66c825c74e0d8e6262cdfe21a6c4b78..c306ca40e658a694c458070d32310f90b154dc85 100644 (file)
@@ -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
        }
 }
index 7dc1566df7e0aa186ea2af31618e73b40d05da27..15e17cd4bac9fe17fa7bff04bd2b5270dfd65284 100644 (file)
@@ -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;