0
0
mirror of https://github.com/XTLS/REALITY.git synced 2025-08-24 15:38:36 +00:00

crypto/internal/fips/tls12: implement TLS 1.2 KDF

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>
This commit is contained in:
yuhan6665 2025-05-04 22:15:27 -04:00
parent 3833e8e2cb
commit 6c71b461ee
2 changed files with 101 additions and 33 deletions

64
prf.go
View File

@ -14,8 +14,12 @@ import (
"errors" "errors"
"fmt" "fmt"
"hash" "hash"
"github.com/xtls/reality/tls12"
) )
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. // Split a premaster secret in two as specified in RFC 4346, Section 5.
func splitPreMasterSecret(secret []byte) (s1, s2 []byte) { func splitPreMasterSecret(secret []byte) (s1, s2 []byte) {
s1 = secret[0 : (len(secret)+1)/2] s1 = secret[0 : (len(secret)+1)/2]
@ -45,7 +49,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. // 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 hashSHA1 := sha1.New
hashMD5 := md5.New hashMD5 := md5.New
@ -61,16 +66,14 @@ func prf10(result, secret, label, seed []byte) {
for i, b := range result2 { for i, b := range result2 {
result[i] ^= b result[i] ^= b
} }
return result
} }
// prf12 implements the TLS 1.2 pseudo-random function, as defined in RFC 5246, Section 5. // 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) { func prf12(hashFunc func() hash.Hash) prfFunc {
return func(result, secret, label, seed []byte) { return func(secret []byte, label string, seed []byte, keyLen int) []byte {
labelAndSeed := make([]byte, len(label)+len(seed)) return tls12.PRF(hashFunc, secret, label, seed, keyLen)
copy(labelAndSeed, label)
copy(labelAndSeed[len(label):], seed)
pHash(result, secret, labelAndSeed, hashFunc)
} }
} }
@ -79,13 +82,13 @@ const (
finishedVerifyLength = 12 // Length of verify_data in a Finished message. finishedVerifyLength = 12 // Length of verify_data in a Finished message.
) )
var masterSecretLabel = []byte("master secret") const masterSecretLabel = "master secret"
var extendedMasterSecretLabel = []byte("extended master secret") const extendedMasterSecretLabel = "extended master secret"
var keyExpansionLabel = []byte("key expansion") const keyExpansionLabel = "key expansion"
var clientFinishedLabel = []byte("client finished") const clientFinishedLabel = "client finished"
var serverFinishedLabel = []byte("server 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 { switch version {
case VersionTLS10, VersionTLS11: case VersionTLS10, VersionTLS11:
return prf10, crypto.Hash(0) return prf10, crypto.Hash(0)
@ -99,7 +102,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) prf, _ := prfAndHashForVersion(version, suite)
return prf return prf
} }
@ -111,17 +114,19 @@ func masterFromPreMasterSecret(version uint16, suite *cipherSuite, preMasterSecr
seed = append(seed, clientRandom...) seed = append(seed, clientRandom...)
seed = append(seed, serverRandom...) seed = append(seed, serverRandom...)
masterSecret := make([]byte, masterSecretLength) return prfForVersion(version, suite)(preMasterSecret, masterSecretLabel, seed, masterSecretLength)
prfForVersion(version, suite)(masterSecret, preMasterSecret, masterSecretLabel, seed)
return masterSecret
} }
// extMasterFromPreMasterSecret generates the extended master secret from the // extMasterFromPreMasterSecret generates the extended master secret from the
// pre-master secret. See RFC 7627. // pre-master secret. See RFC 7627.
func extMasterFromPreMasterSecret(version uint16, suite *cipherSuite, preMasterSecret, transcript []byte) []byte { func extMasterFromPreMasterSecret(version uint16, suite *cipherSuite, preMasterSecret, transcript []byte) []byte {
masterSecret := make([]byte, masterSecretLength) prf, hash := prfAndHashForVersion(version, suite)
prfForVersion(version, suite)(masterSecret, preMasterSecret, extendedMasterSecretLabel, transcript) if version == VersionTLS12 {
return masterSecret // 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 // keysFromMasterSecret generates the connection keys from the master
@ -133,8 +138,7 @@ func keysFromMasterSecret(version uint16, suite *cipherSuite, masterSecret, clie
seed = append(seed, clientRandom...) seed = append(seed, clientRandom...)
n := 2*macLen + 2*keyLen + 2*ivLen n := 2*macLen + 2*keyLen + 2*ivLen
keyMaterial := make([]byte, n) keyMaterial := prfForVersion(version, suite)(masterSecret, keyExpansionLabel, seed, n)
prfForVersion(version, suite)(keyMaterial, masterSecret, keyExpansionLabel, seed)
clientMAC = keyMaterial[:macLen] clientMAC = keyMaterial[:macLen]
keyMaterial = keyMaterial[macLen:] keyMaterial = keyMaterial[macLen:]
serverMAC = keyMaterial[:macLen] serverMAC = keyMaterial[:macLen]
@ -177,7 +181,7 @@ type finishedHash struct {
buffer []byte buffer []byte
version uint16 version uint16
prf func(result, secret, label, seed []byte) prf prfFunc
} }
func (h *finishedHash) Write(msg []byte) (n int, err error) { func (h *finishedHash) Write(msg []byte) (n int, err error) {
@ -209,17 +213,13 @@ func (h finishedHash) Sum() []byte {
// clientSum returns the contents of the verify_data member of a client's // clientSum returns the contents of the verify_data member of a client's
// Finished message. // Finished message.
func (h finishedHash) clientSum(masterSecret []byte) []byte { func (h finishedHash) clientSum(masterSecret []byte) []byte {
out := make([]byte, finishedVerifyLength) return h.prf(masterSecret, clientFinishedLabel, h.Sum(), finishedVerifyLength)
h.prf(out, masterSecret, clientFinishedLabel, h.Sum())
return out
} }
// serverSum returns the contents of the verify_data member of a server's // serverSum returns the contents of the verify_data member of a server's
// Finished message. // Finished message.
func (h finishedHash) serverSum(masterSecret []byte) []byte { func (h finishedHash) serverSum(masterSecret []byte) []byte {
out := make([]byte, finishedVerifyLength) return h.prf(masterSecret, serverFinishedLabel, h.Sum(), finishedVerifyLength)
h.prf(out, masterSecret, serverFinishedLabel, h.Sum())
return out
} }
// hashForClientCertificate returns the handshake messages so far, pre-hashed if // hashForClientCertificate returns the handshake messages so far, pre-hashed if
@ -292,8 +292,6 @@ func ekmFromMasterSecret(version uint16, suite *cipherSuite, masterSecret, clien
seed = append(seed, context...) seed = append(seed, context...)
} }
keyMaterial := make([]byte, length) return prfForVersion(version, suite)(masterSecret, label, seed, length), nil
prfForVersion(version, suite)(keyMaterial, masterSecret, []byte(label), seed)
return keyMaterial, nil
} }
} }

70
tls12/tls12.go Normal file
View File

@ -0,0 +1,70 @@
// 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 (
"github.com/xtls/reality/fips140"
"github.com/xtls/reality/hmac"
// "github.com/xtls/reality/sha256"
// "github.com/xtls/reality/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 fips140.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 fips140.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 fips140.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()
hash()
// switch any(h).(type) {
// case *sha256.Digest:
// if h.Size() != 32 {
// fips140.RecordNonApproved()
// }
// case *sha512.Digest:
// if h.Size() != 46 && h.Size() != 64 {
// fips140.RecordNonApproved()
// }
// default:
// fips140.RecordNonApproved()
// }
return PRF(hash, preMasterSecret, extendedMasterSecretLabel, transcript, masterSecretLength)
}