_ "crypto/internal/fips/sha256"
_ "crypto/internal/fips/sha3"
_ "crypto/internal/fips/sha512"
+ _ "crypto/internal/fips/tls13"
)
func TestCAST(t *testing.T) {
--- /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 tls13
+
+import (
+ "bytes"
+ "crypto/internal/fips"
+ "crypto/internal/fips/sha256"
+ "errors"
+)
+
+func init() {
+ fips.CAST("TLSv1.3-SHA2-256", func() error {
+ input := []byte{
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+ 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10,
+ }
+ want := []byte{
+ 0x78, 0x20, 0x71, 0x75, 0x52, 0xfd, 0x47, 0x67,
+ 0xe1, 0x07, 0x5c, 0x83, 0x74, 0x2e, 0x49, 0x43,
+ 0xf7, 0xe3, 0x08, 0x6a, 0x2a, 0xcb, 0x96, 0xc7,
+ 0xa3, 0x1f, 0xe3, 0x23, 0x56, 0x6e, 0x14, 0x5b,
+ }
+ es := NewEarlySecret(sha256.New, nil)
+ hs := es.HandshakeSecret(nil)
+ ms := hs.MasterSecret()
+ transcript := sha256.New()
+ transcript.Write(input)
+ if got := ms.ResumptionMasterSecret(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 tls13 implements the TLS 1.3 Key Schedule as specified in RFC 8446,
+// Section 7.1 and allowed by FIPS 140-3 IG 2.4.B Resolution 7.
+package tls13
+
+import (
+ "crypto/internal/fips"
+ "crypto/internal/fips/hkdf"
+ "internal/byteorder"
+)
+
+// We don't set the service indicator in this package but we delegate that to
+// the underlying functions because the TLS 1.3 KDF does not have a standard of
+// its own.
+
+// ExpandLabel implements HKDF-Expand-Label from RFC 8446, Section 7.1.
+func ExpandLabel[H fips.Hash](hash func() H, secret []byte, label string, context []byte, length int) []byte {
+ if len("tls13 ")+len(label) > 255 || len(context) > 255 {
+ // It should be impossible for this to panic: labels are fixed strings,
+ // and context is either a fixed-length computed hash, or parsed from a
+ // field which has the same length limitation.
+ //
+ // Another reasonable approach might be to return a randomized slice if
+ // we encounter an error, which would break the connection, but avoid
+ // panicking. This would perhaps be safer but significantly more
+ // confusing to users.
+ panic("tls13: label or context too long")
+ }
+ hkdfLabel := make([]byte, 0, 2+1+len("tls13 ")+len(label)+1+len(context))
+ hkdfLabel = byteorder.BeAppendUint16(hkdfLabel, uint16(length))
+ hkdfLabel = append(hkdfLabel, byte(len("tls13 ")+len(label)))
+ hkdfLabel = append(hkdfLabel, "tls13 "...)
+ hkdfLabel = append(hkdfLabel, label...)
+ hkdfLabel = append(hkdfLabel, byte(len(context)))
+ hkdfLabel = append(hkdfLabel, context...)
+ return hkdf.Expand(hash, secret, hkdfLabel, length)
+}
+
+func extract[H fips.Hash](hash func() H, newSecret, currentSecret []byte) []byte {
+ if newSecret == nil {
+ newSecret = make([]byte, hash().Size())
+ }
+ return hkdf.Extract(hash, newSecret, currentSecret)
+}
+
+func deriveSecret[H fips.Hash](hash func() H, secret []byte, label string, transcript fips.Hash) []byte {
+ if transcript == nil {
+ transcript = hash()
+ }
+ return ExpandLabel(hash, secret, label, transcript.Sum(nil), transcript.Size())
+}
+
+const (
+ resumptionBinderLabel = "res binder"
+ clientEarlyTrafficLabel = "c e traffic"
+ clientHandshakeTrafficLabel = "c hs traffic"
+ serverHandshakeTrafficLabel = "s hs traffic"
+ clientApplicationTrafficLabel = "c ap traffic"
+ serverApplicationTrafficLabel = "s ap traffic"
+ earlyExporterLabel = "e exp master"
+ exporterLabel = "exp master"
+ resumptionLabel = "res master"
+)
+
+type EarlySecret struct {
+ secret []byte
+ hash func() fips.Hash
+}
+
+func NewEarlySecret[H fips.Hash](hash func() H, psk []byte) *EarlySecret {
+ return &EarlySecret{
+ secret: extract(hash, psk, nil),
+ hash: func() fips.Hash { return hash() },
+ }
+}
+
+func (s *EarlySecret) ResumptionBinderKey() []byte {
+ return deriveSecret(s.hash, s.secret, resumptionBinderLabel, nil)
+}
+
+// ClientEarlyTrafficSecret derives the client_early_traffic_secret from the
+// early secret and the transcript up to the ClientHello.
+func (s *EarlySecret) ClientEarlyTrafficSecret(transcript fips.Hash) []byte {
+ return deriveSecret(s.hash, s.secret, clientEarlyTrafficLabel, transcript)
+}
+
+type HandshakeSecret struct {
+ secret []byte
+ hash func() fips.Hash
+}
+
+func (s *EarlySecret) HandshakeSecret(sharedSecret []byte) *HandshakeSecret {
+ derived := deriveSecret(s.hash, s.secret, "derived", nil)
+ return &HandshakeSecret{
+ secret: extract(s.hash, sharedSecret, derived),
+ hash: s.hash,
+ }
+}
+
+// ClientHandshakeTrafficSecret derives the client_handshake_traffic_secret from
+// the handshake secret and the transcript up to the ServerHello.
+func (s *HandshakeSecret) ClientHandshakeTrafficSecret(transcript fips.Hash) []byte {
+ return deriveSecret(s.hash, s.secret, clientHandshakeTrafficLabel, transcript)
+}
+
+// ServerHandshakeTrafficSecret derives the server_handshake_traffic_secret from
+// the handshake secret and the transcript up to the ServerHello.
+func (s *HandshakeSecret) ServerHandshakeTrafficSecret(transcript fips.Hash) []byte {
+ return deriveSecret(s.hash, s.secret, serverHandshakeTrafficLabel, transcript)
+}
+
+type MasterSecret struct {
+ secret []byte
+ hash func() fips.Hash
+}
+
+func (s *HandshakeSecret) MasterSecret() *MasterSecret {
+ derived := deriveSecret(s.hash, s.secret, "derived", nil)
+ return &MasterSecret{
+ secret: extract(s.hash, nil, derived),
+ hash: s.hash,
+ }
+}
+
+// ClientApplicationTrafficSecret derives the client_application_traffic_secret_0
+// from the master secret and the transcript up to the server Finished.
+func (s *MasterSecret) ClientApplicationTrafficSecret(transcript fips.Hash) []byte {
+ return deriveSecret(s.hash, s.secret, clientApplicationTrafficLabel, transcript)
+}
+
+// ServerApplicationTrafficSecret derives the server_application_traffic_secret_0
+// from the master secret and the transcript up to the server Finished.
+func (s *MasterSecret) ServerApplicationTrafficSecret(transcript fips.Hash) []byte {
+ return deriveSecret(s.hash, s.secret, serverApplicationTrafficLabel, transcript)
+}
+
+// ResumptionMasterSecret derives the resumption_master_secret from the master secret
+// and the transcript up to the client Finished.
+func (s *MasterSecret) ResumptionMasterSecret(transcript fips.Hash) []byte {
+ return deriveSecret(s.hash, s.secret, resumptionLabel, transcript)
+}
+
+type ExporterMasterSecret struct {
+ secret []byte
+ hash func() fips.Hash
+}
+
+// ExporterMasterSecret derives the exporter_master_secret from the master secret
+// and the transcript up to the server Finished.
+func (s *MasterSecret) ExporterMasterSecret(transcript fips.Hash) *ExporterMasterSecret {
+ return &ExporterMasterSecret{
+ secret: deriveSecret(s.hash, s.secret, exporterLabel, transcript),
+ hash: s.hash,
+ }
+}
+
+// EarlyExporterMasterSecret derives the exporter_master_secret from the early secret
+// and the transcript up to the ClientHello.
+func (s *EarlySecret) EarlyExporterMasterSecret(transcript fips.Hash) *ExporterMasterSecret {
+ return &ExporterMasterSecret{
+ secret: deriveSecret(s.hash, s.secret, earlyExporterLabel, transcript),
+ hash: s.hash,
+ }
+}
+
+func (s *ExporterMasterSecret) Exporter(label string, context []byte, length int) []byte {
+ secret := deriveSecret(s.hash, s.secret, label, nil)
+ h := s.hash()
+ h.Write(context)
+ return ExpandLabel(s.hash, secret, "exporter", h.Sum(nil), length)
+}
+
+func TestingOnlyExporterSecret(s *ExporterMasterSecret) []byte {
+ return s.secret
+}
"crypto"
"crypto/ecdsa"
"crypto/ed25519"
+ "crypto/internal/fips/tls13"
"crypto/internal/hpke"
"crypto/internal/mlkem768"
"crypto/rsa"
if err := transcriptMsg(hello, transcript); err != nil {
return err
}
- earlyTrafficSecret := suite.deriveSecret(earlySecret, clientEarlyTrafficLabel, transcript)
+ earlyTrafficSecret := earlySecret.ClientEarlyTrafficSecret(transcript)
c.quicSetWriteSecret(QUICEncryptionLevelEarly, suite.id, earlyTrafficSecret)
}
}
func (c *Conn) loadSession(hello *clientHelloMsg) (
- session *SessionState, earlySecret, binderKey []byte, err error) {
+ session *SessionState, earlySecret *tls13.EarlySecret, binderKey []byte, err error) {
if c.config.SessionTicketsDisabled || c.config.ClientSessionCache == nil {
return nil, nil, nil, nil
}
hello.pskBinders = [][]byte{make([]byte, cipherSuite.hash.Size())}
// Compute the PSK binders. See RFC 8446, Section 4.2.11.2.
- earlySecret = cipherSuite.extract(session.secret, nil)
- binderKey = cipherSuite.deriveSecret(earlySecret, resumptionBinderLabel, nil)
+ earlySecret = tls13.NewEarlySecret(cipherSuite.hash.New, session.secret)
+ binderKey = earlySecret.ResumptionBinderKey()
transcript := cipherSuite.hash.New()
if err := computeAndUpdatePSK(hello, binderKey, transcript, cipherSuite.finishedHash); err != nil {
return nil, nil, nil, err
"context"
"crypto"
"crypto/hmac"
+ "crypto/internal/fips/hkdf"
+ "crypto/internal/fips/tls13"
"crypto/internal/mlkem768"
"crypto/rsa"
"crypto/subtle"
keyShareKeys *keySharePrivateKeys
session *SessionState
- earlySecret []byte
+ earlySecret *tls13.EarlySecret
binderKey []byte
certReq *certificateRequestMsgTLS13
sentDummyCCS bool
suite *cipherSuiteTLS13
transcript hash.Hash
- masterSecret []byte
+ masterSecret *tls13.MasterSecret
trafficSecret []byte // client_application_traffic_secret_0
echContext *echContext
confTranscript.Write(hs.serverHello.original[:30])
confTranscript.Write(make([]byte, 8))
confTranscript.Write(hs.serverHello.original[38:])
- acceptConfirmation := hs.suite.expandLabel(
- hs.suite.extract(hs.echContext.innerHello.random, nil),
+ acceptConfirmation := tls13.ExpandLabel(hs.suite.hash.New,
+ hkdf.Extract(hs.suite.hash.New, hs.echContext.innerHello.random, nil),
"ech accept confirmation",
confTranscript.Sum(nil),
8,
copy(hrrHello, hs.serverHello.original)
hrrHello = bytes.Replace(hrrHello, hs.serverHello.encryptedClientHello, make([]byte, 8), 1)
confTranscript.Write(hrrHello)
- acceptConfirmation := hs.suite.expandLabel(
- hs.suite.extract(hs.echContext.innerHello.random, nil),
+ acceptConfirmation := tls13.ExpandLabel(hs.suite.hash.New,
+ hkdf.Extract(hs.suite.hash.New, hs.echContext.innerHello.random, nil),
"hrr ech accept confirmation",
confTranscript.Sum(nil),
8,
earlySecret := hs.earlySecret
if !hs.usingPSK {
- earlySecret = hs.suite.extract(nil, nil)
+ earlySecret = tls13.NewEarlySecret(hs.suite.hash.New, nil)
}
- handshakeSecret := hs.suite.extract(sharedKey,
- hs.suite.deriveSecret(earlySecret, "derived", nil))
+ handshakeSecret := earlySecret.HandshakeSecret(sharedKey)
- clientSecret := hs.suite.deriveSecret(handshakeSecret,
- clientHandshakeTrafficLabel, hs.transcript)
+ clientSecret := handshakeSecret.ClientHandshakeTrafficSecret(hs.transcript)
c.out.setTrafficSecret(hs.suite, QUICEncryptionLevelHandshake, clientSecret)
- serverSecret := hs.suite.deriveSecret(handshakeSecret,
- serverHandshakeTrafficLabel, hs.transcript)
+ serverSecret := handshakeSecret.ServerHandshakeTrafficSecret(hs.transcript)
c.in.setTrafficSecret(hs.suite, QUICEncryptionLevelHandshake, serverSecret)
if c.quic != nil {
return err
}
- hs.masterSecret = hs.suite.extract(nil,
- hs.suite.deriveSecret(handshakeSecret, "derived", nil))
+ hs.masterSecret = handshakeSecret.MasterSecret()
return nil
}
// Derive secrets that take context through the server Finished.
- hs.trafficSecret = hs.suite.deriveSecret(hs.masterSecret,
- clientApplicationTrafficLabel, hs.transcript)
- serverSecret := hs.suite.deriveSecret(hs.masterSecret,
- serverApplicationTrafficLabel, hs.transcript)
+ hs.trafficSecret = hs.masterSecret.ClientApplicationTrafficSecret(hs.transcript)
+ serverSecret := hs.masterSecret.ServerApplicationTrafficSecret(hs.transcript)
c.in.setTrafficSecret(hs.suite, QUICEncryptionLevelApplication, serverSecret)
err = c.config.writeKeyLog(keyLogLabelClientTraffic, hs.hello.random, hs.trafficSecret)
c.out.setTrafficSecret(hs.suite, QUICEncryptionLevelApplication, hs.trafficSecret)
if !c.config.SessionTicketsDisabled && c.config.ClientSessionCache != nil {
- c.resumptionSecret = hs.suite.deriveSecret(hs.masterSecret,
- resumptionLabel, hs.transcript)
+ c.resumptionSecret = hs.masterSecret.ResumptionMasterSecret(hs.transcript)
}
if c.quic != nil {
return c.sendAlert(alertInternalError)
}
- psk := cipherSuite.expandLabel(c.resumptionSecret, "resumption",
+ psk := tls13.ExpandLabel(cipherSuite.hash.New, c.resumptionSecret, "resumption",
msg.nonce, cipherSuite.hash.Size())
session := c.sessionState()
"context"
"crypto"
"crypto/hmac"
+ "crypto/internal/fips/tls13"
"crypto/internal/mlkem768"
"crypto/rsa"
"errors"
suite *cipherSuiteTLS13
cert *Certificate
sigAlg SignatureScheme
- earlySecret []byte
+ earlySecret *tls13.EarlySecret
sharedKey []byte
- handshakeSecret []byte
- masterSecret []byte
+ handshakeSecret *tls13.HandshakeSecret
+ masterSecret *tls13.MasterSecret
trafficSecret []byte // client_application_traffic_secret_0
transcript hash.Hash
clientFinished []byte
}
}
- hs.earlySecret = hs.suite.extract(sessionState.secret, nil)
- binderKey := hs.suite.deriveSecret(hs.earlySecret, resumptionBinderLabel, nil)
+ hs.earlySecret = tls13.NewEarlySecret(hs.suite.hash.New, sessionState.secret)
+ binderKey := hs.earlySecret.ResumptionBinderKey()
// Clone the transcript in case a HelloRetryRequest was recorded.
transcript := cloneHash(hs.transcript, hs.suite.hash)
if transcript == nil {
if err := transcriptMsg(hs.clientHello, transcript); err != nil {
return err
}
- earlyTrafficSecret := hs.suite.deriveSecret(hs.earlySecret, clientEarlyTrafficLabel, transcript)
+ earlyTrafficSecret := hs.earlySecret.ClientEarlyTrafficSecret(transcript)
c.quicSetReadSecret(QUICEncryptionLevelEarly, hs.suite.id, earlyTrafficSecret)
}
earlySecret := hs.earlySecret
if earlySecret == nil {
- earlySecret = hs.suite.extract(nil, nil)
+ earlySecret = tls13.NewEarlySecret(hs.suite.hash.New, nil)
}
- hs.handshakeSecret = hs.suite.extract(hs.sharedKey,
- hs.suite.deriveSecret(earlySecret, "derived", nil))
+ hs.handshakeSecret = earlySecret.HandshakeSecret(hs.sharedKey)
- clientSecret := hs.suite.deriveSecret(hs.handshakeSecret,
- clientHandshakeTrafficLabel, hs.transcript)
+ clientSecret := hs.handshakeSecret.ClientHandshakeTrafficSecret(hs.transcript)
c.in.setTrafficSecret(hs.suite, QUICEncryptionLevelHandshake, clientSecret)
- serverSecret := hs.suite.deriveSecret(hs.handshakeSecret,
- serverHandshakeTrafficLabel, hs.transcript)
+ serverSecret := hs.handshakeSecret.ServerHandshakeTrafficSecret(hs.transcript)
c.out.setTrafficSecret(hs.suite, QUICEncryptionLevelHandshake, serverSecret)
if c.quic != nil {
// Derive secrets that take context through the server Finished.
- hs.masterSecret = hs.suite.extract(nil,
- hs.suite.deriveSecret(hs.handshakeSecret, "derived", nil))
+ hs.masterSecret = hs.handshakeSecret.MasterSecret()
- hs.trafficSecret = hs.suite.deriveSecret(hs.masterSecret,
- clientApplicationTrafficLabel, hs.transcript)
- serverSecret := hs.suite.deriveSecret(hs.masterSecret,
- serverApplicationTrafficLabel, hs.transcript)
+ hs.trafficSecret = hs.masterSecret.ClientApplicationTrafficSecret(hs.transcript)
+ serverSecret := hs.masterSecret.ServerApplicationTrafficSecret(hs.transcript)
c.out.setTrafficSecret(hs.suite, QUICEncryptionLevelApplication, serverSecret)
if c.quic != nil {
return err
}
- c.resumptionSecret = hs.suite.deriveSecret(hs.masterSecret,
- resumptionLabel, hs.transcript)
+ c.resumptionSecret = hs.masterSecret.ResumptionMasterSecret(hs.transcript)
if !hs.shouldSendSessionTickets() {
return nil
}
// ticket_nonce, which must be unique per connection, is always left at
// zero because we only ever send one ticket per connection.
- psk := suite.expandLabel(c.resumptionSecret, "resumption",
+ psk := tls13.ExpandLabel(suite.hash.New, c.resumptionSecret, "resumption",
nil, suite.hash.Size())
m := new(newSessionTicketMsgTLS13)
import (
"crypto/ecdh"
"crypto/hmac"
+ "crypto/internal/fips/tls13"
"crypto/internal/mlkem768"
"errors"
- "fmt"
"hash"
"io"
- "golang.org/x/crypto/cryptobyte"
- "golang.org/x/crypto/hkdf"
"golang.org/x/crypto/sha3"
)
// This file contains the functions necessary to compute the TLS 1.3 key
// schedule. See RFC 8446, Section 7.
-const (
- resumptionBinderLabel = "res binder"
- clientEarlyTrafficLabel = "c e traffic"
- clientHandshakeTrafficLabel = "c hs traffic"
- serverHandshakeTrafficLabel = "s hs traffic"
- clientApplicationTrafficLabel = "c ap traffic"
- serverApplicationTrafficLabel = "s ap traffic"
- exporterLabel = "exp master"
- resumptionLabel = "res master"
- trafficUpdateLabel = "traffic upd"
-)
-
-// expandLabel implements HKDF-Expand-Label from RFC 8446, Section 7.1.
-func (c *cipherSuiteTLS13) expandLabel(secret []byte, label string, context []byte, length int) []byte {
- var hkdfLabel cryptobyte.Builder
- hkdfLabel.AddUint16(uint16(length))
- hkdfLabel.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) {
- b.AddBytes([]byte("tls13 "))
- b.AddBytes([]byte(label))
- })
- hkdfLabel.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) {
- b.AddBytes(context)
- })
- hkdfLabelBytes, err := hkdfLabel.Bytes()
- if err != nil {
- // Rather than calling BytesOrPanic, we explicitly handle this error, in
- // order to provide a reasonable error message. It should be basically
- // impossible for this to panic, and routing errors back through the
- // tree rooted in this function is quite painful. The labels are fixed
- // size, and the context is either a fixed-length computed hash, or
- // parsed from a field which has the same length limitation. As such, an
- // error here is likely to only be caused during development.
- //
- // NOTE: another reasonable approach here might be to return a
- // randomized slice if we encounter an error, which would break the
- // connection, but avoid panicking. This would perhaps be safer but
- // significantly more confusing to users.
- panic(fmt.Errorf("failed to construct HKDF label: %s", err))
- }
- out := make([]byte, length)
- n, err := hkdf.Expand(c.hash.New, secret, hkdfLabelBytes).Read(out)
- if err != nil || n != length {
- panic("tls: HKDF-Expand-Label invocation failed unexpectedly")
- }
- return out
-}
-
-// deriveSecret implements Derive-Secret from RFC 8446, Section 7.1.
-func (c *cipherSuiteTLS13) deriveSecret(secret []byte, label string, transcript hash.Hash) []byte {
- if transcript == nil {
- transcript = c.hash.New()
- }
- return c.expandLabel(secret, label, transcript.Sum(nil), c.hash.Size())
-}
-
-// extract implements HKDF-Extract with the cipher suite hash.
-func (c *cipherSuiteTLS13) extract(newSecret, currentSecret []byte) []byte {
- if newSecret == nil {
- newSecret = make([]byte, c.hash.Size())
- }
- return hkdf.Extract(c.hash.New, newSecret, currentSecret)
-}
-
// nextTrafficSecret generates the next traffic secret, given the current one,
// according to RFC 8446, Section 7.2.
func (c *cipherSuiteTLS13) nextTrafficSecret(trafficSecret []byte) []byte {
- return c.expandLabel(trafficSecret, trafficUpdateLabel, nil, c.hash.Size())
+ return tls13.ExpandLabel(c.hash.New, trafficSecret, "traffic upd", nil, c.hash.Size())
}
// trafficKey generates traffic keys according to RFC 8446, Section 7.3.
func (c *cipherSuiteTLS13) trafficKey(trafficSecret []byte) (key, iv []byte) {
- key = c.expandLabel(trafficSecret, "key", nil, c.keyLen)
- iv = c.expandLabel(trafficSecret, "iv", nil, aeadNonceLength)
+ key = tls13.ExpandLabel(c.hash.New, trafficSecret, "key", nil, c.keyLen)
+ iv = tls13.ExpandLabel(c.hash.New, trafficSecret, "iv", nil, aeadNonceLength)
return
}
// to RFC 8446, Section 4.4.4. See sections 4.4 and 4.2.11.2 for the baseKey
// selection.
func (c *cipherSuiteTLS13) finishedHash(baseKey []byte, transcript hash.Hash) []byte {
- finishedKey := c.expandLabel(baseKey, "finished", nil, c.hash.Size())
+ finishedKey := tls13.ExpandLabel(c.hash.New, baseKey, "finished", nil, c.hash.Size())
verifyData := hmac.New(c.hash.New, finishedKey)
verifyData.Write(transcript.Sum(nil))
return verifyData.Sum(nil)
// exportKeyingMaterial implements RFC5705 exporters for TLS 1.3 according to
// RFC 8446, Section 7.5.
-func (c *cipherSuiteTLS13) exportKeyingMaterial(masterSecret []byte, transcript hash.Hash) func(string, []byte, int) ([]byte, error) {
- expMasterSecret := c.deriveSecret(masterSecret, exporterLabel, transcript)
+func (c *cipherSuiteTLS13) exportKeyingMaterial(s *tls13.MasterSecret, transcript hash.Hash) func(string, []byte, int) ([]byte, error) {
+ expMasterSecret := s.ExporterMasterSecret(transcript)
return func(label string, context []byte, length int) ([]byte, error) {
- secret := c.deriveSecret(expMasterSecret, label, nil)
- h := c.hash.New()
- h.Write(context)
- return c.expandLabel(secret, "exporter", h.Sum(nil), length), nil
+ return expMasterSecret.Exporter(label, context, length), nil
}
}
import (
"bytes"
+ "crypto/internal/fips/tls13"
"crypto/internal/mlkem768"
+ "crypto/sha256"
"encoding/hex"
- "hash"
"strings"
"testing"
"unicode"
)
+func TestACVPVectors(t *testing.T) {
+ // https://github.com/usnistgov/ACVP-Server/blob/3a7333f63/gen-val/json-files/TLS-v1.3-KDF-RFC8446/prompt.json#L428-L436
+ psk := fromHex("56288B726C73829F7A3E47B103837C8139ACF552E7530C7A710B35ED41191698")
+ dhe := fromHex("EFFE9EC26AA29FD750DFA6A10B944D74071595B27EE88887D5E11C84590B5CC3")
+ helloClientRandom := fromHex("E9137679E582BA7C1DB41CF725F86C6D09C8C05F297BAD9A65B552EAF524FDE4")
+ helloServerRandom := fromHex("23ECCFD030790748C8F8D8A656FD98D717F1B62AF3712F97211D2070B499F98A")
+ finishedClientRandom := fromHex("62A62FA75563ED4FDCAA0BC16567B314871C304ACF06B0FFC3F08C1797594D43")
+ finishedServerRandom := fromHex("C750EDA6696CD101B142BD79E00E6AC8C5F2C0ABC78DD64F4D991326659E9299")
+
+ // https://github.com/usnistgov/ACVP-Server/blob/3a7333f63/gen-val/json-files/TLS-v1.3-KDF-RFC8446/expectedResults.json#L571-L581
+ clientEarlyTrafficSecret := fromHex("3272189698C3594D18F58EFA3F12B638A249515099BE7A2FA9836BABE74F0111")
+ earlyExporterMasterSecret := fromHex("88E078F562CDC930219F6A5E98A1CE8C6E5F3DAC5AC516459A96F2EF8F114C66")
+ clientHandshakeTrafficSecret := fromHex("B32306C3CE9932C460A1FE6C0F060593974842036B96FA45049B7352E71C2AD2")
+ serverHandshakeTrafficSecret := fromHex("22787F8CA269D34BC549AC8BA19F2040938A3AA370D7CC9D60F720882B88D01B")
+ clientApplicationTrafficSecret := fromHex("47D7EA08397B5871154B0FE85584BCC30A87C69E84D69B56007C5B21F76493BA")
+ serverApplicationTrafficSecret := fromHex("EFBDB0C873C0480DA57307083839A8984BE25B9A8545E4FCA029940FE2800565")
+ exporterMasterSecret := fromHex("8A43D787EE3804EAD4A2A5B32972F9896B696295645D7222E1FD081DDD939834")
+ resumptionMasterSecret := fromHex("5F4C961329C91044011ACBECB0B289282E0E3FED045CB3EA924DFFE5FE654B3D")
+
+ // The "Random" values are undocumented, but they are meant to be written to
+ // the hash in sequence to develop the transcript.
+ transcript := sha256.New()
+
+ es := tls13.NewEarlySecret(sha256.New, psk)
+
+ transcript.Write(helloClientRandom)
+
+ if got := es.ClientEarlyTrafficSecret(transcript); !bytes.Equal(got, clientEarlyTrafficSecret) {
+ t.Errorf("clientEarlyTrafficSecret = %x, want %x", got, clientEarlyTrafficSecret)
+ }
+ if got := tls13.TestingOnlyExporterSecret(es.EarlyExporterMasterSecret(transcript)); !bytes.Equal(got, earlyExporterMasterSecret) {
+ t.Errorf("earlyExporterMasterSecret = %x, want %x", got, earlyExporterMasterSecret)
+ }
+
+ hs := es.HandshakeSecret(dhe)
+
+ transcript.Write(helloServerRandom)
+
+ if got := hs.ClientHandshakeTrafficSecret(transcript); !bytes.Equal(got, clientHandshakeTrafficSecret) {
+ t.Errorf("clientHandshakeTrafficSecret = %x, want %x", got, clientHandshakeTrafficSecret)
+ }
+ if got := hs.ServerHandshakeTrafficSecret(transcript); !bytes.Equal(got, serverHandshakeTrafficSecret) {
+ t.Errorf("serverHandshakeTrafficSecret = %x, want %x", got, serverHandshakeTrafficSecret)
+ }
+
+ ms := hs.MasterSecret()
+
+ transcript.Write(finishedServerRandom)
+
+ if got := ms.ClientApplicationTrafficSecret(transcript); !bytes.Equal(got, clientApplicationTrafficSecret) {
+ t.Errorf("clientApplicationTrafficSecret = %x, want %x", got, clientApplicationTrafficSecret)
+ }
+ if got := ms.ServerApplicationTrafficSecret(transcript); !bytes.Equal(got, serverApplicationTrafficSecret) {
+ t.Errorf("serverApplicationTrafficSecret = %x, want %x", got, serverApplicationTrafficSecret)
+ }
+ if got := tls13.TestingOnlyExporterSecret(ms.ExporterMasterSecret(transcript)); !bytes.Equal(got, exporterMasterSecret) {
+ t.Errorf("exporterMasterSecret = %x, want %x", got, exporterMasterSecret)
+ }
+
+ transcript.Write(finishedClientRandom)
+
+ if got := ms.ResumptionMasterSecret(transcript); !bytes.Equal(got, resumptionMasterSecret) {
+ t.Errorf("resumptionMasterSecret = %x, want %x", got, resumptionMasterSecret)
+ }
+}
+
// This file contains tests derived from draft-ietf-tls-tls13-vectors-07.
func parseVector(v string) []byte {
return res
}
-func TestDeriveSecret(t *testing.T) {
- chTranscript := cipherSuitesTLS13[0].hash.New()
- chTranscript.Write(parseVector(`
- payload (512 octets): 01 00 01 fc 03 03 1b c3 ce b6 bb e3 9c ff
- 93 83 55 b5 a5 0a db 6d b2 1b 7a 6a f6 49 d7 b4 bc 41 9d 78 76
- 48 7d 95 00 00 06 13 01 13 03 13 02 01 00 01 cd 00 00 00 0b 00
- 09 00 00 06 73 65 72 76 65 72 ff 01 00 01 00 00 0a 00 14 00 12
- 00 1d 00 17 00 18 00 19 01 00 01 01 01 02 01 03 01 04 00 33 00
- 26 00 24 00 1d 00 20 e4 ff b6 8a c0 5f 8d 96 c9 9d a2 66 98 34
- 6c 6b e1 64 82 ba dd da fe 05 1a 66 b4 f1 8d 66 8f 0b 00 2a 00
- 00 00 2b 00 03 02 03 04 00 0d 00 20 00 1e 04 03 05 03 06 03 02
- 03 08 04 08 05 08 06 04 01 05 01 06 01 02 01 04 02 05 02 06 02
- 02 02 00 2d 00 02 01 01 00 1c 00 02 40 01 00 15 00 57 00 00 00
- 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
- 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
- 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
- 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
- 00 29 00 dd 00 b8 00 b2 2c 03 5d 82 93 59 ee 5f f7 af 4e c9 00
- 00 00 00 26 2a 64 94 dc 48 6d 2c 8a 34 cb 33 fa 90 bf 1b 00 70
- ad 3c 49 88 83 c9 36 7c 09 a2 be 78 5a bc 55 cd 22 60 97 a3 a9
- 82 11 72 83 f8 2a 03 a1 43 ef d3 ff 5d d3 6d 64 e8 61 be 7f d6
- 1d 28 27 db 27 9c ce 14 50 77 d4 54 a3 66 4d 4e 6d a4 d2 9e e0
- 37 25 a6 a4 da fc d0 fc 67 d2 ae a7 05 29 51 3e 3d a2 67 7f a5
- 90 6c 5b 3f 7d 8f 92 f2 28 bd a4 0d da 72 14 70 f9 fb f2 97 b5
- ae a6 17 64 6f ac 5c 03 27 2e 97 07 27 c6 21 a7 91 41 ef 5f 7d
- e6 50 5e 5b fb c3 88 e9 33 43 69 40 93 93 4a e4 d3 57 fa d6 aa
- cb 00 21 20 3a dd 4f b2 d8 fd f8 22 a0 ca 3c f7 67 8e f5 e8 8d
- ae 99 01 41 c5 92 4d 57 bb 6f a3 1b 9e 5f 9d`))
-
- type args struct {
- secret []byte
- label string
- transcript hash.Hash
- }
- tests := []struct {
- name string
- args args
- want []byte
- }{
- {
- `derive secret for handshake "tls13 derived"`,
- args{
- parseVector(`PRK (32 octets): 33 ad 0a 1c 60 7e c0 3b 09 e6 cd 98 93 68 0c e2
- 10 ad f3 00 aa 1f 26 60 e1 b2 2e 10 f1 70 f9 2a`),
- "derived",
- nil,
- },
- parseVector(`expanded (32 octets): 6f 26 15 a1 08 c7 02 c5 67 8f 54 fc 9d ba
- b6 97 16 c0 76 18 9c 48 25 0c eb ea c3 57 6c 36 11 ba`),
- },
- {
- `derive secret "tls13 c e traffic"`,
- args{
- parseVector(`PRK (32 octets): 9b 21 88 e9 b2 fc 6d 64 d7 1d c3 29 90 0e 20 bb
- 41 91 50 00 f6 78 aa 83 9c bb 79 7c b7 d8 33 2c`),
- "c e traffic",
- chTranscript,
- },
- parseVector(`expanded (32 octets): 3f bb e6 a6 0d eb 66 c3 0a 32 79 5a ba 0e
- ff 7e aa 10 10 55 86 e7 be 5c 09 67 8d 63 b6 ca ab 62`),
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- c := cipherSuitesTLS13[0]
- if got := c.deriveSecret(tt.args.secret, tt.args.label, tt.args.transcript); !bytes.Equal(got, tt.want) {
- t.Errorf("cipherSuiteTLS13.deriveSecret() = % x, want % x", got, tt.want)
- }
- })
- }
-}
-
func TestTrafficKey(t *testing.T) {
trafficSecret := parseVector(
`PRK (32 octets): b6 7b 7d 69 0c c1 6c 4e 75 e5 42 13 cb 2d 37 b4
}
}
-func TestExtract(t *testing.T) {
- type args struct {
- newSecret []byte
- currentSecret []byte
- }
- tests := []struct {
- name string
- args args
- want []byte
- }{
- {
- `extract secret "early"`,
- args{
- nil,
- nil,
- },
- parseVector(`secret (32 octets): 33 ad 0a 1c 60 7e c0 3b 09 e6 cd 98 93 68 0c
- e2 10 ad f3 00 aa 1f 26 60 e1 b2 2e 10 f1 70 f9 2a`),
- },
- {
- `extract secret "master"`,
- args{
- nil,
- parseVector(`salt (32 octets): 43 de 77 e0 c7 77 13 85 9a 94 4d b9 db 25 90 b5
- 31 90 a6 5b 3e e2 e4 f1 2d d7 a0 bb 7c e2 54 b4`),
- },
- parseVector(`secret (32 octets): 18 df 06 84 3d 13 a0 8b f2 a4 49 84 4c 5f 8a
- 47 80 01 bc 4d 4c 62 79 84 d5 a4 1d a8 d0 40 29 19`),
- },
- {
- `extract secret "handshake"`,
- args{
- parseVector(`IKM (32 octets): 8b d4 05 4f b5 5b 9d 63 fd fb ac f9 f0 4b 9f 0d
- 35 e6 d6 3f 53 75 63 ef d4 62 72 90 0f 89 49 2d`),
- parseVector(`salt (32 octets): 6f 26 15 a1 08 c7 02 c5 67 8f 54 fc 9d ba b6 97
- 16 c0 76 18 9c 48 25 0c eb ea c3 57 6c 36 11 ba`),
- },
- parseVector(`secret (32 octets): 1d c8 26 e9 36 06 aa 6f dc 0a ad c1 2f 74 1b
- 01 04 6a a6 b9 9f 69 1e d2 21 a9 f0 ca 04 3f be ac`),
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- c := cipherSuitesTLS13[0]
- if got := c.extract(tt.args.newSecret, tt.args.currentSecret); !bytes.Equal(got, tt.want) {
- t.Errorf("cipherSuiteTLS13.extract() = % x, want % x", got, tt.want)
- }
- })
- }
-}
-
func TestKyberDecapsulate(t *testing.T) {
// From https://pq-crystals.org/kyber/data/kyber-submission-nist-round3.zip
dkBytes, _ := hex.DecodeString("07638FB69868F3D320E5862BD96933FEB311B362093C9B5D50170BCED43F1B536D9A204BB1F22695950BA1F2A9E8EB828B284488760B3FC84FABA04275D5628E39C5B2471374283C503299C0AB49B66B8BBB56A4186624F919A2BA59BB08D8551880C2BEFC4F87F25F59AB587A79C327D792D54C974A69262FF8A78938289E9A87B688B083E0595FE218B6BB1505941CE2E81A5A64C5AAC60417256985349EE47A52420A5F97477B7236AC76BC70E8288729287EE3E34A3DBC3683C0B7B10029FC203418537E7466BA6385A8FF301EE12708F82AAA1E380FC7A88F8F205AB7E88D7E95952A55BA20D09B79A47141D62BF6EB7DD307B08ECA13A5BC5F6B68581C6865B27BBCDDAB142F4B2CBFF488C8A22705FAA98A2B9EEA3530C76662335CC7EA3A00777725EBCCCD2A4636B2D9122FF3AB77123CE0883C1911115E50C9E8A94194E48DD0D09CFFB3ADCD2C1E92430903D07ADBF00532031575AA7F9E7B5A1F3362DEC936D4043C05F2476C07578BC9CBAF2AB4E382727AD41686A96B2548820BB03B32F11B2811AD62F489E951632ABA0D1DF89680CC8A8B53B481D92A68D70B4EA1C3A6A561C0692882B5CA8CC942A8D495AFCB06DE89498FB935B775908FE7A03E324D54CC19D4E1AABD3593B38B19EE1388FE492B43127E5A504253786A0D69AD32601C28E2C88504A5BA599706023A61363E17C6B9BB59BDC697452CD059451983D738CA3FD034E3F5988854CA05031DB09611498988197C6B30D258DFE26265541C89A4B31D6864E9389B03CB74F7EC4323FB9421A4B9790A26D17B0398A26767350909F84D57B6694DF830664CA8B3C3C03ED2AE67B89006868A68527CCD666459AB7F056671000C6164D3A7F266A14D97CBD7004D6C92CACA770B844A4FA9B182E7B18CA885082AC5646FCB4A14E1685FEB0C9CE3372AB95365C04FD83084F80A23FF10A05BF15F7FA5ACC6C0CB462C33CA524FA6B8BB359043BA68609EAA2536E81D08463B19653B5435BA946C9ADDEB202B04B031CC960DCC12E4518D428B32B257A4FC7313D3A7980D80082E934F9D95C32B0A0191A23604384DD9E079BBBAA266D14C3F756B9F2133107433A4E83FA7187282A809203A4FAF841851833D121AC383843A5E55BC2381425E16C7DB4CC9AB5C1B0D91A47E2B8DE0E582C86B6B0D907BB360B97F40AB5D038F6B75C814B27D9B968D419832BC8C2BEE605EF6E5059D33100D90485D378450014221736C07407CAC260408AA64926619788B8601C2A752D1A6CBF820D7C7A04716203225B3895B9342D147A8185CFC1BB65BA06B4142339903C0AC4651385B45D98A8B19D28CD6BAB088787F7EE1B12461766B43CBCCB96434427D93C065550688F6948ED1B5475A425F1B85209D061C08B56C1CC069F6C0A7C6F29358CAB911087732A649D27C9B98F9A48879387D9B00C25959A71654D6F6A946164513E47A75D005986C2363C09F6B537ECA78B9303A5FA457608A586A653A347DB04DFCC19175B3A301172536062A658A95277570C8852CA8973F4AE123A334047DD711C8927A634A03388A527B034BF7A8170FA702C1F7C23EC32D18A2374890BE9C787A9409C82D192C4BB705A2F996CE405DA72C2D9C843EE9F8313ECC7F86D6294D59159D9A879A542E260922ADF999051CC45200C9FFDB60449C49465979272367C083A7D6267A3ED7A7FD47957C219327F7CA73A4007E1627F00B11CC80573C15AEE6640FB8562DFA6B240CA0AD351AC4AC155B96C14C8AB13DD262CDFD51C4BB5572FD616553D17BDD430ACBEA3E95F0B698D66990AB51E5D03783A8B3D278A5720454CF9695CFDCA08485BA099C51CD92A7EA7587C1D15C28E609A81852601B0604010679AA482D51261EC36E36B8719676217FD74C54786488F4B4969C05A8BA27CA3A77CCE73B965923CA554E422B9B61F4754641608AC16C9B8587A32C1C5DD788F88B36B717A46965635DEB67F45B129B99070909C93EB80B42C2B3F3F70343A7CF37E8520E7BCFC416ACA4F18C7981262BA2BFC756AE03278F0EC66DC2057696824BA6769865A601D7148EF6F54E5AF5686AA2906F994CE38A5E0B938F239007003022C03392DF3401B1E4A3A7EBC6161449F73374C8B0140369343D9295FDF511845C4A46EBAAB6CA5492F6800B98C0CC803653A4B1D6E6AAED1932BACC5FEFAA818BA502859BA5494C5F5402C8536A9C4C1888150617F80098F6B2A99C39BC5DC7CF3B5900A21329AB59053ABAA64ED163E859A8B3B3CA3359B750CCC3E710C7AC43C8191CB5D68870C06391C0CB8AEC72B897AC6BE7FBAACC676ED66314C83630E89448C88A1DF04ACEB23ABF2E409EF333C622289C18A2134E650C45257E47475FA33AA537A5A8F7680214716C50D470E3284963CA64F54677AEC54B5272162BF52BC8142E1D4183FC017454A6B5A496831759064024745978CBD51A6CEDC8955DE4CC6D363670A47466E82BE5C23603A17BF22ACDB7CC984AF08C87E14E27753CF587A8EC3447E62C649E887A67C36C9CE98721B697213275646B194F36758673A8ED11284455AFC7A8529F69C97A3C2D7B8C636C0BA55614B768E624E712930F776169B01715725351BC74B47395ED52B25A1313C95164814C34C979CBDFAB85954662CAB485E75087A98CC74BB82CA2D1B5BF2803238480638C40E90B43C7460E7AA917F010151FAB1169987B372ABB59271F7006C24E60236B84B9DDD600623704254617FB498D89E58B0368BCB2103E79353EB587860C1422E476162E425BC2381DB82C6592737E1DD602864B0167A71EC1F223305C02FE25052AF2B3B5A55A0D7A2022D9A798DC0C5874A98702AAF4054C5D80338A5248B5B7BD09C53B5E2A084B047D277A861B1A73BB51488DE04EF573C85230A0470B73175C9FA50594F66A5F50B4150054C93B68186F8B5CBC49316C8548A642B2B36A1D454C7489AC33B2D2CE6668096782A2C1E0866D21A65E16B585E7AF8618BDF3184C1986878508917277B93E10706B1614972B2A94C7310FE9C708C231A1A8AC8D9314A529A97F469BF64962D820648443099A076D55D4CEA824A58304844F99497C10A25148618A315D72CA857D1B04D575B94F85C01D19BEF211BF0AA3362E7041FD16596D808E867B44C4C00D1CDA3418967717F147D0EB21B42AAEE74AC35D0B92414B958531AADF463EC6305AE5ECAF79174002F26DDECC813BF32672E8529D95A4E730A7AB4A3E8F8A8AF979A665EAFD465FC64A0C5F8F3F9003489415899D59A543D8208C54A3166529B53922D4EC143B50F01423B177895EDEE22BB739F647ECF85F50BC25EF7B5A725DEE868626ED79D451140800E03B59B956F8210E556067407D13DC90FA9E8B872BFB8F")
< crypto/internal/fips/hmac
< crypto/internal/fips/check
< crypto/internal/fips/hkdf
+ < crypto/internal/fips/tls13
< FIPS;
FIPS < crypto/internal/fips/check/checktest;