mirror of
https://github.com/golang/go
synced 2024-11-26 03:57:57 -07: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:
parent
743746a3a5
commit
c9408a154a
@ -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"
|
||||
)
|
||||
|
||||
|
37
src/crypto/internal/fips/tls12/cast.go
Normal file
37
src/crypto/internal/fips/tls12/cast.go
Normal file
@ -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
|
||||
})
|
||||
}
|
69
src/crypto/internal/fips/tls12/tls12.go
Normal file
69
src/crypto/internal/fips/tls12/tls12.go
Normal file
@ -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)
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user