update: rm cf KEMkey & KeySharesParams in TLS13OnlyState

These can be set and read via KeySharePrivateKeys. While the breakage is unfortunate for backwards compatibility, it is already unavoidable in one direction: the cloudflare kem key does not store the mlkem seed and is therefore incompatible with crypto/mlkem.
This commit is contained in:
Mingye Chen 2025-03-01 19:18:24 -07:00
parent 9fada94f7e
commit d24af4ae55
5 changed files with 55 additions and 146 deletions

View file

@ -381,8 +381,6 @@ func (c *Conn) clientHandshake(ctx context.Context) (err error) {
earlySecret: earlySecret, earlySecret: earlySecret,
binderKey: binderKey, binderKey: binderKey,
echContext: ech, echContext: ech,
keySharesParams: NewKeySharesParameters(), // [uTLS]
} }
return hs.handshake() return hs.handshake()
} }

View file

@ -8,7 +8,6 @@ import (
"bytes" "bytes"
"context" "context"
"crypto" "crypto"
"crypto/ecdh"
"crypto/hmac" "crypto/hmac"
"crypto/mlkem" "crypto/mlkem"
"crypto/rsa" "crypto/rsa"
@ -19,74 +18,16 @@ import (
"slices" "slices"
"time" "time"
"github.com/cloudflare/circl/kem"
"github.com/refraction-networking/utls/internal/hkdf" "github.com/refraction-networking/utls/internal/hkdf"
"github.com/refraction-networking/utls/internal/tls13" "github.com/refraction-networking/utls/internal/tls13"
) )
// [uTLS SECTION START]
// KeySharesParameters serves as a in-memory storage for generated keypairs by UTLS when generating
// ClientHello. It is used to store both ecdhe and kem keypairs.
type KeySharesParameters struct {
ecdhePrivKeymap map[CurveID]*ecdh.PrivateKey
ecdhePubKeymap map[CurveID]*ecdh.PublicKey
// based on cloudflare/go
kemPrivKeymap map[CurveID]kem.PrivateKey
kemPubKeymap map[CurveID]kem.PublicKey
}
func NewKeySharesParameters() *KeySharesParameters {
return &KeySharesParameters{
ecdhePrivKeymap: make(map[CurveID]*ecdh.PrivateKey),
ecdhePubKeymap: make(map[CurveID]*ecdh.PublicKey),
kemPrivKeymap: make(map[CurveID]kem.PrivateKey),
kemPubKeymap: make(map[CurveID]kem.PublicKey),
}
}
func (ksp *KeySharesParameters) AddEcdheKeypair(curveID CurveID, ecdheKey *ecdh.PrivateKey, ecdhePubKey *ecdh.PublicKey) {
ksp.ecdhePrivKeymap[curveID] = ecdheKey
ksp.ecdhePubKeymap[curveID] = ecdhePubKey
}
func (ksp *KeySharesParameters) GetEcdheKey(curveID CurveID) (ecdheKey *ecdh.PrivateKey, ok bool) {
ecdheKey, ok = ksp.ecdhePrivKeymap[curveID]
return
}
func (ksp *KeySharesParameters) GetEcdhePubkey(curveID CurveID) (params *ecdh.PublicKey, ok bool) {
params, ok = ksp.ecdhePubKeymap[curveID]
return
}
func (ksp *KeySharesParameters) AddKemKeypair(curveID CurveID, kemKey kem.PrivateKey, kemPubKey kem.PublicKey) {
// if curveID == x25519Kyber768Draft00 { // only store for x25519Kyber768Draft00
// ksp.kemPrivKeymap[curveID] = kemKey
// ksp.kemPubKeymap[curveID] = kemPubKey
// }
}
func (ksp *KeySharesParameters) GetKemKey(curveID CurveID) (kemKey kem.PrivateKey, ok bool) {
kemKey, ok = ksp.kemPrivKeymap[curveID]
return
}
func (ksp *KeySharesParameters) GetKemPubkey(curveID CurveID) (params kem.PublicKey, ok bool) {
params, ok = ksp.kemPubKeymap[curveID]
return
}
// [uTLS SECTION END]
type clientHandshakeStateTLS13 struct { type clientHandshakeStateTLS13 struct {
c *Conn c *Conn
ctx context.Context ctx context.Context
serverHello *serverHelloMsg serverHello *serverHelloMsg
hello *clientHelloMsg hello *clientHelloMsg
keyShareKeys *keySharePrivateKeys keyShareKeys *keySharePrivateKeys
keySharesParams *KeySharesParameters // [uTLS] support both ecdhe and kem
session *SessionState session *SessionState
earlySecret *tls13.EarlySecret earlySecret *tls13.EarlySecret
@ -117,24 +58,6 @@ func (hs *clientHandshakeStateTLS13) handshake() error {
return errors.New("tls: server selected TLS 1.3 in a renegotiation") return errors.New("tls: server selected TLS 1.3 in a renegotiation")
} }
// [uTLS SECTION START]
// if hs.keyShareKeys == nil {
// // set echdheParams to what we received from server
// if ecdheKey, ok := hs.keySharesParams.GetEcdheKey(hs.serverHello.serverShare.group); ok {
// hs.keyShareKeys.ecdhe = ecdheKey
// hs.keyShareKeys.curveID = hs.serverHello.serverShare.group
// }
// // set kemParams to what we received from server
// if kemKey, ok := hs.keySharesParams.GetKemKey(hs.serverHello.serverShare.group); ok {
// if kyberKey, ecdhKey, err := mlkemCirclToGo(kemKey); err == nil {
// hs.keyShareKeys.kyber = kyberKey
// hs.keyShareKeys.ecdhe = ecdhKey
// hs.keyShareKeys.curveID = hs.serverHello.serverShare.group
// }
// }
// }
// [uTLS SECTION END]
// Consistency check on the presence of a keyShare and its parameters. // Consistency check on the presence of a keyShare and its parameters.
if hs.keyShareKeys == nil || hs.keyShareKeys.ecdhe == nil || len(hs.hello.keyShares) == 0 { if hs.keyShareKeys == nil || hs.keyShareKeys.ecdhe == nil || len(hs.hello.keyShares) == 0 {
return c.sendAlert(alertInternalError) return c.sendAlert(alertInternalError)

View file

@ -1,5 +1,11 @@
package tls package tls
import (
"crypto/ecdh"
"github.com/cloudflare/circl/kem"
)
// This file contains all the alias functions, symbols, names, etc. that // This file contains all the alias functions, symbols, names, etc. that
// was once used in the old version of the library. This is to ensure // was once used in the old version of the library. This is to ensure
// backwards compatibility with the old version of the library. // backwards compatibility with the old version of the library.
@ -10,3 +16,26 @@ package tls
// //
// Deprecated: Use ExtendedMasterSecretExtension instead. // Deprecated: Use ExtendedMasterSecretExtension instead.
type UtlsExtendedMasterSecretExtension = ExtendedMasterSecretExtension type UtlsExtendedMasterSecretExtension = ExtendedMasterSecretExtension
// Deprecated: Use KeySharePrivateKeys instead. This type is not used and will be removed in the future.
// KeySharesParameters serves as a in-memory storage for generated keypairs by UTLS when generating
// ClientHello. It is used to store both ecdhe and kem keypairs.
type KeySharesParameters struct{}
func NewKeySharesParameters() *KeySharesParameters { return &KeySharesParameters{} }
func (*KeySharesParameters) AddEcdheKeypair(curveID CurveID, ecdheKey *ecdh.PrivateKey, ecdhePubKey *ecdh.PublicKey) {
return
}
func (*KeySharesParameters) GetEcdheKey(curveID CurveID) (ecdheKey *ecdh.PrivateKey, ok bool) { return }
func (*KeySharesParameters) GetEcdhePubkey(curveID CurveID) (params *ecdh.PublicKey, ok bool) { return }
func (*KeySharesParameters) AddKemKeypair(curveID CurveID, kemKey kem.PrivateKey, kemPubKey kem.PublicKey) {
return
}
func (ksp *KeySharesParameters) GetKemKey(curveID CurveID) (kemKey kem.PrivateKey, ok bool) { return }
func (ksp *KeySharesParameters) GetKemPubkey(curveID CurveID) (params kem.PublicKey, ok bool) { return }

View file

@ -2636,7 +2636,6 @@ func (uconn *UConn) ApplyPreset(p *ClientHelloSpec) error {
} else { } else {
uconn.HandshakeState.State13.KeyShareKeys = &KeySharePrivateKeys{} uconn.HandshakeState.State13.KeyShareKeys = &KeySharePrivateKeys{}
} }
uconn.HandshakeState.State13.KeySharesParams = NewKeySharesParameters()
uconn.echCtx = ech uconn.echCtx = ech
hello := uconn.HandshakeState.Hello hello := uconn.HandshakeState.Hello
@ -2750,12 +2749,6 @@ func (uconn *UConn) ApplyPreset(p *ClientHelloSpec) error {
return err return err
} }
// circlKyberKey, err := kyberGoToCircl(kyberKey, ecdheKey)
// if err != nil {
// return err
// }
// uconn.HandshakeState.State13.KeySharesParams.AddKemKeypair(curveID, circlKyberKey, circlKyberKey.Public())
if curveID == X25519Kyber768Draft00 { if curveID == X25519Kyber768Draft00 {
ext.KeyShares[i].Data = append(ecdheKey.PublicKey().Bytes(), mlkemKey.EncapsulationKey().Bytes()...) ext.KeyShares[i].Data = append(ecdheKey.PublicKey().Bytes(), mlkemKey.EncapsulationKey().Bytes()...)
} else { } else {
@ -2770,7 +2763,6 @@ func (uconn *UConn) ApplyPreset(p *ClientHelloSpec) error {
if len(ext.KeyShares) > i+1 && ext.KeyShares[i+1].Group == X25519 { if len(ext.KeyShares) > i+1 && ext.KeyShares[i+1].Group == X25519 {
// Reuse the same X25519 ephemeral key for both keyshares, as allowed by draft-ietf-tls-hybrid-design-09, Section 3.2. // Reuse the same X25519 ephemeral key for both keyshares, as allowed by draft-ietf-tls-hybrid-design-09, Section 3.2.
uconn.HandshakeState.State13.KeyShareKeys.Ecdhe = ecdheKey uconn.HandshakeState.State13.KeyShareKeys.Ecdhe = ecdheKey
// uconn.HandshakeState.State13.KeySharesParams.AddEcdheKeypair(curveID, ecdheKey, ecdheKey.PublicKey())
ext.KeyShares[i+1].Data = ecdheKey.PublicKey().Bytes() ext.KeyShares[i+1].Data = ecdheKey.PublicKey().Bytes()
} }
} else { } else {
@ -2780,8 +2772,6 @@ func (uconn *UConn) ApplyPreset(p *ClientHelloSpec) error {
"To mimic it, fill the Data(key) field manually", curveID) "To mimic it, fill the Data(key) field manually", curveID)
} }
// uconn.HandshakeState.State13.KeySharesParams.AddEcdheKeypair(curveID, ecdheKey, ecdheKey.PublicKey())
ext.KeyShares[i].Data = ecdheKey.PublicKey().Bytes() ext.KeyShares[i].Data = ecdheKey.PublicKey().Bytes()
if !preferredCurveIsSet { if !preferredCurveIsSet {
// only do this once for the first non-grease curve // only do this once for the first non-grease curve

View file

@ -41,9 +41,13 @@ type PubClientHandshakeState struct {
type TLS13OnlyState struct { type TLS13OnlyState struct {
// Deprecated: Use KeyShareKeys instead. KeyShareKeys will take precedence if both are set. // Deprecated: Use KeyShareKeys instead. KeyShareKeys will take precedence if both are set.
// Support may be removed in the future. // Support may be removed in the future.
EcdheKey *ecdh.PrivateKey EcdheKey *ecdh.PrivateKey
// Deprecated: Use KeyShareKeys instead. This variable is no longer used.
// Will be removed in the future.
KeySharesParams *KeySharesParameters KeySharesParams *KeySharesParameters
KEMKey *KemPrivateKey // Deprecated: Use KeyShareKeys instead. This variable is no longer used.
// Will be removed in the future.
KEMKey *KemPrivateKey
KeyShareKeys *KeySharePrivateKeys KeyShareKeys *KeySharePrivateKeys
Suite *PubCipherSuiteTLS13 Suite *PubCipherSuiteTLS13
@ -62,46 +66,11 @@ type TLS12OnlyState struct {
Suite PubCipherSuite Suite PubCipherSuite
} }
// func mlkemCirclToGo(circlKey kem.PrivateKey) (*mlkem768.DecapsulationKey, *ecdh.PrivateKey, error) {
// if circlKey.Scheme().Name() != "Kyber768-X25519" {
// return nil, nil, fmt.Errorf("circl key is not Kyber768-X25519")
// }
// encodedKey, err := circlKey.MarshalBinary()
// if err != nil {
// return nil, nil, err
// }
// ecdhKey := encodedKey[:x25519PublicKeySize]
// kyberKey := encodedKey[x25519PublicKeySize:]
// goKyberkey, err := mlkem768.NewKeyFromExtendedEncoding(kyberKey)
// if err != nil {
// return nil, nil, err
// }
// goEcdhKey, err := ecdh.X25519().NewPrivateKey(ecdhKey)
// if err != nil {
// return nil, nil, err
// }
// return goKyberkey, goEcdhKey, nil
// }
func (chs *TLS13OnlyState) private13KeyShareKeys() *keySharePrivateKeys { func (chs *TLS13OnlyState) private13KeyShareKeys() *keySharePrivateKeys {
if chs.KeyShareKeys != nil { if chs.KeyShareKeys != nil {
return chs.KeyShareKeys.ToPrivate() return chs.KeyShareKeys.ToPrivate()
} }
// if chs.KEMKey != nil {
// if kyberKey, ecdhKey, err := mlkemCirclToGo(chs.KEMKey.SecretKey); err == nil {
// return &keySharePrivateKeys{
// kyber: kyberKey,
// ecdhe: ecdhKey,
// }
// }
// }
if chs.EcdheKey != nil { if chs.EcdheKey != nil {
return &keySharePrivateKeys{ return &keySharePrivateKeys{
ecdhe: chs.EcdheKey, ecdhe: chs.EcdheKey,
@ -120,11 +89,10 @@ func (chs *PubClientHandshakeState) toPrivate13() *clientHandshakeStateTLS13 {
return nil return nil
} else { } else {
return &clientHandshakeStateTLS13{ return &clientHandshakeStateTLS13{
c: chs.C, c: chs.C,
serverHello: chs.ServerHello.getPrivatePtr(), serverHello: chs.ServerHello.getPrivatePtr(),
hello: chs.Hello.getPrivatePtr(), hello: chs.Hello.getPrivatePtr(),
keyShareKeys: chs.State13.private13KeyShareKeys(), keyShareKeys: chs.State13.private13KeyShareKeys(),
keySharesParams: chs.State13.KeySharesParams,
session: chs.Session, session: chs.Session,
binderKey: chs.State13.BinderKey, binderKey: chs.State13.BinderKey,
@ -146,16 +114,15 @@ func (chs13 *clientHandshakeStateTLS13) toPublic13() *PubClientHandshakeState {
return nil return nil
} else { } else {
tls13State := TLS13OnlyState{ tls13State := TLS13OnlyState{
KeySharesParams: chs13.keySharesParams, KeyShareKeys: chs13.keyShareKeys.ToPublic(),
KeyShareKeys: chs13.keyShareKeys.ToPublic(), EarlySecret: chs13.earlySecret.Secret(),
EarlySecret: chs13.earlySecret.Secret(), BinderKey: chs13.binderKey,
BinderKey: chs13.binderKey, CertReq: chs13.certReq.toPublic(),
CertReq: chs13.certReq.toPublic(), UsingPSK: chs13.usingPSK,
UsingPSK: chs13.usingPSK, SentDummyCCS: chs13.sentDummyCCS,
SentDummyCCS: chs13.sentDummyCCS, Suite: chs13.suite.toPublic(),
Suite: chs13.suite.toPublic(), TrafficSecret: chs13.trafficSecret,
TrafficSecret: chs13.trafficSecret, Transcript: chs13.transcript,
Transcript: chs13.transcript,
} }
return &PubClientHandshakeState{ return &PubClientHandshakeState{
C: chs13.c, C: chs13.c,
@ -891,6 +858,8 @@ type kemPrivateKey struct {
curveID CurveID curveID CurveID
} }
// Deprecated: Use KeySharePrivateKeys instead. This type is no longer used.
// Will be removed in the future.
type KemPrivateKey struct { type KemPrivateKey struct {
SecretKey kem.PrivateKey SecretKey kem.PrivateKey
CurveID CurveID CurveID CurveID