mirror of
https://github.com/XTLS/REALITY.git
synced 2026-10-07 06:18:06 +03:00
Sync upstream Go 1.27+; Add "[REALITY] SECTION" comments (#30)
https://github.com/XTLS/REALITY/pull/30#issuecomment-5579024460 https://github.com/XTLS/REALITY/pull/30#issuecomment-5594872257 https://github.com/XTLS/REALITY/pull/38#issuecomment-5752892932
This commit is contained in:
+235
-21
@@ -5,7 +5,9 @@
|
||||
package reality
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/ecdh"
|
||||
"crypto/fips140"
|
||||
"crypto/hmac"
|
||||
"crypto/mlkem"
|
||||
"errors"
|
||||
@@ -51,35 +53,247 @@ func (c *cipherSuiteTLS13) exportKeyingMaterial(s *tls13.MasterSecret, transcrip
|
||||
}
|
||||
|
||||
type keySharePrivateKeys struct {
|
||||
curveID CurveID
|
||||
ecdhe *ecdh.PrivateKey
|
||||
mlkem *mlkem.DecapsulationKey768
|
||||
ecdhe *ecdh.PrivateKey
|
||||
mlkem crypto.Decapsulator
|
||||
}
|
||||
|
||||
const x25519PublicKeySize = 32
|
||||
// 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)
|
||||
|
||||
// generateECDHEKey returns a PrivateKey that implements Diffie-Hellman
|
||||
// according to RFC 8446, Section 4.2.8.2.
|
||||
func generateECDHEKey(rand io.Reader, curveID CurveID) (*ecdh.PrivateKey, error) {
|
||||
curve, ok := curveForCurveID(curveID)
|
||||
if !ok {
|
||||
return nil, errors.New("tls: internal error: unsupported curve")
|
||||
// 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) {
|
||||
mlkemGenerateKey768 := func() (crypto.Decapsulator, error) {
|
||||
return mlkem.GenerateKey768()
|
||||
}
|
||||
mlkemGenerateKey1024 := func() (crypto.Decapsulator, error) {
|
||||
return mlkem.GenerateKey1024()
|
||||
}
|
||||
mlkemNewPublicKey768 := func(b []byte) (crypto.Encapsulator, error) {
|
||||
return mlkem.NewEncapsulationKey768(b)
|
||||
}
|
||||
mlkemNewPublicKey1024 := func(b []byte) (crypto.Encapsulator, error) {
|
||||
return mlkem.NewEncapsulationKey1024(b)
|
||||
}
|
||||
|
||||
return curve.GenerateKey(rand)
|
||||
}
|
||||
|
||||
func curveForCurveID(id CurveID) (ecdh.Curve, bool) {
|
||||
switch id {
|
||||
case X25519:
|
||||
return ecdh.X25519(), true
|
||||
return &ecdhKeyExchange{id, ecdh.X25519()}, nil
|
||||
case CurveP256:
|
||||
return ecdh.P256(), true
|
||||
return &ecdhKeyExchange{id, ecdh.P256()}, nil
|
||||
case CurveP384:
|
||||
return ecdh.P384(), true
|
||||
return &ecdhKeyExchange{id, ecdh.P384()}, nil
|
||||
case CurveP521:
|
||||
return ecdh.P521(), true
|
||||
return &ecdhKeyExchange{id, ecdh.P521()}, nil
|
||||
case X25519MLKEM768:
|
||||
return &hybridKeyExchange{id, ecdhKeyExchange{X25519, ecdh.X25519()},
|
||||
32, mlkem.EncapsulationKeySize768, mlkem.CiphertextSize768,
|
||||
mlkemGenerateKey768, mlkemNewPublicKey768}, nil
|
||||
case SecP256r1MLKEM768:
|
||||
return &hybridKeyExchange{id, ecdhKeyExchange{CurveP256, ecdh.P256()},
|
||||
65, mlkem.EncapsulationKeySize768, mlkem.CiphertextSize768,
|
||||
mlkemGenerateKey768, mlkemNewPublicKey768}, nil
|
||||
case SecP384r1MLKEM1024:
|
||||
return &hybridKeyExchange{id, ecdhKeyExchange{CurveP384, ecdh.P384()},
|
||||
97, mlkem.EncapsulationKeySize1024, mlkem.CiphertextSize1024,
|
||||
mlkemGenerateKey1024, mlkemNewPublicKey1024}, nil
|
||||
case MLKEM1024:
|
||||
return &mlkem1024KeyExchange{}, nil
|
||||
default:
|
||||
return nil, false
|
||||
return nil, errors.New("tls: unsupported key exchange")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type mlkem1024KeyExchange struct{}
|
||||
|
||||
func (ke *mlkem1024KeyExchange) keyShares(_ io.Reader) (*keySharePrivateKeys, []keyShare, error) {
|
||||
priv, err := mlkem.GenerateKey1024()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return &keySharePrivateKeys{mlkem: priv}, []keyShare{{MLKEM1024, priv.EncapsulationKey().Bytes()}}, nil
|
||||
}
|
||||
|
||||
func (ke *mlkem1024KeyExchange) serverSharedSecret(_ io.Reader, clientKeyShare []byte) ([]byte, keyShare, error) {
|
||||
peerKey, err := mlkem.NewEncapsulationKey1024(clientKeyShare)
|
||||
if err != nil {
|
||||
return nil, keyShare{}, err
|
||||
}
|
||||
sharedKey, keyShareData := peerKey.Encapsulate()
|
||||
return sharedKey, keyShare{MLKEM1024, keyShareData}, nil
|
||||
}
|
||||
|
||||
func (ke *mlkem1024KeyExchange) clientSharedSecret(priv *keySharePrivateKeys, serverKeyShare []byte) ([]byte, error) {
|
||||
sharedKey, err := priv.mlkem.Decapsulate(serverKeyShare)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return sharedKey, nil
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
mlkemGenerateKey func() (crypto.Decapsulator, error)
|
||||
mlkemNewPublicKey func([]byte) (crypto.Encapsulator, error)
|
||||
}
|
||||
|
||||
func (ke *hybridKeyExchange) keyShares(rand io.Reader) (*keySharePrivateKeys, []keyShare, error) {
|
||||
var (
|
||||
priv *keySharePrivateKeys
|
||||
ecdhShares []keyShare
|
||||
err error
|
||||
)
|
||||
fips140.WithoutEnforcement(func() { // Hybrid of ML-KEM, which is Approved.
|
||||
priv, ecdhShares, err = ke.ecdh.keyShares(rand)
|
||||
})
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
priv.mlkem, err = ke.mlkemGenerateKey()
|
||||
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:]
|
||||
}
|
||||
var (
|
||||
ecdhSharedSecret []byte
|
||||
ks keyShare
|
||||
err error
|
||||
)
|
||||
fips140.WithoutEnforcement(func() { // Hybrid of ML-KEM, which is Approved.
|
||||
ecdhSharedSecret, ks, err = ke.ecdh.serverSharedSecret(rand, ecdhShareData)
|
||||
})
|
||||
if err != nil {
|
||||
return nil, keyShare{}, err
|
||||
}
|
||||
mlkemPeerKey, err := ke.mlkemNewPublicKey(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:]
|
||||
}
|
||||
var (
|
||||
ecdhSharedSecret []byte
|
||||
err error
|
||||
)
|
||||
fips140.WithoutEnforcement(func() { // Hybrid of ML-KEM, which is Approved.
|
||||
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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user