// Copyright 2018 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 tls import ( "crypto" "crypto/ecdh" "crypto/hmac" "crypto/internal/fips140/tls13" "crypto/mlkem" "errors" "hash" "io" ) // This file contains the functions necessary to compute the TLS 1.3 key // schedule. See RFC 8446, Section 7. // 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 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 = tls13.ExpandLabel(c.hash.New, trafficSecret, "key", nil, c.keyLen) iv = tls13.ExpandLabel(c.hash.New, trafficSecret, "iv", nil, aeadNonceLength) return } // finishedHash generates the Finished verify_data or PskBinderEntry according // 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 := 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(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) { return expMasterSecret.Exporter(label, context, length), nil } } type keySharePrivateKeys struct { ecdhe *ecdh.PrivateKey mlkem crypto.Decapsulator } // A keyExchange implements a TLS 1.3 KEM. type keyExchange interface { // keyShares generates one or two key shares. // // The first one will match the id, the second (if present) reuses the // traditional component of the requested hybrid, as allowed by // draft-ietf-tls-hybrid-design-09, Section 3.2. keyShares(rand io.Reader) (*keySharePrivateKeys, []keyShare, error) // serverSharedSecret computes the shared secret and the server's key share. serverSharedSecret(rand io.Reader, clientKeyShare []byte) ([]byte, keyShare, error) // clientSharedSecret computes the shared secret given the server's key // share and the keys generated by keyShares. clientSharedSecret(priv *keySharePrivateKeys, serverKeyShare []byte) ([]byte, error) } func keyExchangeForCurveID(id CurveID) (keyExchange, error) { newMLKEMPrivateKey768 := func(b []byte) (crypto.Decapsulator, error) { return mlkem.NewDecapsulationKey768(b) } newMLKEMPrivateKey1024 := func(b []byte) (crypto.Decapsulator, error) { return mlkem.NewDecapsulationKey1024(b) } newMLKEMPublicKey768 := func(b []byte) (crypto.Encapsulator, error) { return mlkem.NewEncapsulationKey768(b) } newMLKEMPublicKey1024 := func(b []byte) (crypto.Encapsulator, error) { return mlkem.NewEncapsulationKey1024(b) } switch id { case X25519: return &ecdhKeyExchange{id, ecdh.X25519()}, nil case CurveP256: return &ecdhKeyExchange{id, ecdh.P256()}, nil case CurveP384: return &ecdhKeyExchange{id, ecdh.P384()}, nil case CurveP521: return &ecdhKeyExchange{id, ecdh.P521()}, nil case X25519MLKEM768: return &hybridKeyExchange{id, ecdhKeyExchange{X25519, ecdh.X25519()}, 32, mlkem.EncapsulationKeySize768, mlkem.CiphertextSize768, newMLKEMPrivateKey768, newMLKEMPublicKey768}, nil case SecP256r1MLKEM768: return &hybridKeyExchange{id, ecdhKeyExchange{CurveP256, ecdh.P256()}, 65, mlkem.EncapsulationKeySize768, mlkem.CiphertextSize768, newMLKEMPrivateKey768, newMLKEMPublicKey768}, nil case SecP384r1MLKEM1024: return &hybridKeyExchange{id, ecdhKeyExchange{CurveP384, ecdh.P384()}, 97, mlkem.EncapsulationKeySize1024, mlkem.CiphertextSize1024, newMLKEMPrivateKey1024, newMLKEMPublicKey1024}, nil default: return nil, errors.New("tls: unsupported key exchange") } } type ecdhKeyExchange struct { id CurveID curve ecdh.Curve } func (ke *ecdhKeyExchange) keyShares(rand io.Reader) (*keySharePrivateKeys, []keyShare, error) { priv, err := ke.curve.GenerateKey(rand) if err != nil { return nil, nil, err } return &keySharePrivateKeys{ecdhe: priv}, []keyShare{{ke.id, priv.PublicKey().Bytes()}}, nil } func (ke *ecdhKeyExchange) serverSharedSecret(rand io.Reader, clientKeyShare []byte) ([]byte, keyShare, error) { key, err := ke.curve.GenerateKey(rand) if err != nil { return nil, keyShare{}, err } peerKey, err := ke.curve.NewPublicKey(clientKeyShare) if err != nil { return nil, keyShare{}, err } sharedKey, err := key.ECDH(peerKey) if err != nil { return nil, keyShare{}, err } return sharedKey, keyShare{ke.id, key.PublicKey().Bytes()}, nil } func (ke *ecdhKeyExchange) clientSharedSecret(priv *keySharePrivateKeys, serverKeyShare []byte) ([]byte, error) { peerKey, err := ke.curve.NewPublicKey(serverKeyShare) if err != nil { return nil, err } sharedKey, err := priv.ecdhe.ECDH(peerKey) if err != nil { return nil, err } return sharedKey, nil } type hybridKeyExchange struct { id CurveID ecdh ecdhKeyExchange ecdhElementSize int mlkemPublicKeySize int mlkemCiphertextSize int newMLKEMPrivateKey func([]byte) (crypto.Decapsulator, error) newMLKEMPublicKey func([]byte) (crypto.Encapsulator, error) } func (ke *hybridKeyExchange) keyShares(rand io.Reader) (*keySharePrivateKeys, []keyShare, error) { priv, ecdhShares, err := ke.ecdh.keyShares(rand) if err != nil { return nil, nil, err } seed := make([]byte, mlkem.SeedSize) if _, err := io.ReadFull(rand, seed); err != nil { return nil, nil, err } priv.mlkem, err = ke.newMLKEMPrivateKey(seed) if err != nil { return nil, nil, err } var shareData []byte // For X25519MLKEM768, the ML-KEM-768 encapsulation key comes first. // For SecP256r1MLKEM768 and SecP384r1MLKEM1024, the ECDH share comes first. // See draft-ietf-tls-ecdhe-mlkem-02, Section 4.1. if ke.id == X25519MLKEM768 { shareData = append(priv.mlkem.Encapsulator().Bytes(), ecdhShares[0].data...) } else { shareData = append(ecdhShares[0].data, priv.mlkem.Encapsulator().Bytes()...) } return priv, []keyShare{{ke.id, shareData}, ecdhShares[0]}, nil } func (ke *hybridKeyExchange) serverSharedSecret(rand io.Reader, clientKeyShare []byte) ([]byte, keyShare, error) { if len(clientKeyShare) != ke.ecdhElementSize+ke.mlkemPublicKeySize { return nil, keyShare{}, errors.New("tls: invalid client key share length for hybrid key exchange") } var ecdhShareData, mlkemShareData []byte if ke.id == X25519MLKEM768 { mlkemShareData = clientKeyShare[:ke.mlkemPublicKeySize] ecdhShareData = clientKeyShare[ke.mlkemPublicKeySize:] } else { ecdhShareData = clientKeyShare[:ke.ecdhElementSize] mlkemShareData = clientKeyShare[ke.ecdhElementSize:] } ecdhSharedSecret, ks, err := ke.ecdh.serverSharedSecret(rand, ecdhShareData) if err != nil { return nil, keyShare{}, err } mlkemPeerKey, err := ke.newMLKEMPublicKey(mlkemShareData) if err != nil { return nil, keyShare{}, err } mlkemSharedSecret, mlkemKeyShare := mlkemPeerKey.Encapsulate() var sharedKey []byte if ke.id == X25519MLKEM768 { sharedKey = append(mlkemSharedSecret, ecdhSharedSecret...) ks.data = append(mlkemKeyShare, ks.data...) } else { sharedKey = append(ecdhSharedSecret, mlkemSharedSecret...) ks.data = append(ks.data, mlkemKeyShare...) } ks.group = ke.id return sharedKey, ks, nil } func (ke *hybridKeyExchange) clientSharedSecret(priv *keySharePrivateKeys, serverKeyShare []byte) ([]byte, error) { if len(serverKeyShare) != ke.ecdhElementSize+ke.mlkemCiphertextSize { return nil, errors.New("tls: invalid server key share length for hybrid key exchange") } var ecdhShareData, mlkemShareData []byte if ke.id == X25519MLKEM768 { mlkemShareData = serverKeyShare[:ke.mlkemCiphertextSize] ecdhShareData = serverKeyShare[ke.mlkemCiphertextSize:] } else { ecdhShareData = serverKeyShare[:ke.ecdhElementSize] mlkemShareData = serverKeyShare[ke.ecdhElementSize:] } ecdhSharedSecret, err := ke.ecdh.clientSharedSecret(priv, ecdhShareData) if err != nil { return nil, err } mlkemSharedSecret, err := priv.mlkem.Decapsulate(mlkemShareData) if err != nil { return nil, err } var sharedKey []byte if ke.id == X25519MLKEM768 { sharedKey = append(mlkemSharedSecret, ecdhSharedSecret...) } else { sharedKey = append(ecdhSharedSecret, mlkemSharedSecret...) } return sharedKey, nil }