|
|
@@ -21,6 +21,7 @@ import ( |
|
|
|
"sync/atomic" |
|
|
|
"time" |
|
|
|
|
|
|
|
sidh "github_com/cloudflare/sidh/sidh" |
|
|
|
"golang_org/x/crypto/curve25519" |
|
|
|
) |
|
|
|
|
|
|
@@ -29,11 +30,16 @@ import ( |
|
|
|
const numSessionTickets = 2 |
|
|
|
|
|
|
|
type secretLabel int |
|
|
|
type role uint8 |
|
|
|
|
|
|
|
const ( |
|
|
|
kRole_Server = iota |
|
|
|
kRole_Client |
|
|
|
x25519SharedSecretSz = 32 |
|
|
|
|
|
|
|
P503PubKeySz = 378 |
|
|
|
P503PrvKeySz = 32 |
|
|
|
P503SharedSecretSz = 126 |
|
|
|
SidhP503Curve25519PubKeySz = x25519SharedSecretSz + P503PubKeySz |
|
|
|
SidhP503Curve25519PrvKeySz = x25519SharedSecretSz + P503PrvKeySz |
|
|
|
SidhP503Curve25519SharedKeySz = x25519SharedSecretSz + P503SharedSecretSz |
|
|
|
) |
|
|
|
|
|
|
|
const ( |
|
|
@@ -55,6 +61,38 @@ type keySchedule13 struct { |
|
|
|
config *Config // Used for KeyLogWriter callback, nil if keylogging is disabled. |
|
|
|
} |
|
|
|
|
|
|
|
// Interface implemented by DH key exchange strategies |
|
|
|
type dhKex interface { |
|
|
|
// c - context of current TLS handshake, groupId - ID of an algorithm |
|
|
|
// (curve/field) being chosen for key agreement. Methods implmenting an |
|
|
|
// interface always assume that provided groupId is correct. |
|
|
|
// |
|
|
|
// In case of success, function returns secret key and ephemeral key. Otherwise |
|
|
|
// error is set. |
|
|
|
generate(c *Conn, groupId CurveID) ([]byte, keyShare, error) |
|
|
|
// c - context of current TLS handshake, ks - public key received |
|
|
|
// from the other side of the connection, secretKey - is a private key |
|
|
|
// used for DH key agreement. Function returns shared secret in case |
|
|
|
// of success or empty slice otherwise. |
|
|
|
derive(c *Conn, ks keyShare, secretKey []byte) []byte |
|
|
|
} |
|
|
|
|
|
|
|
// Key Exchange strategies per curve type |
|
|
|
type kexNist struct{} // Used by NIST curves; P-256, P-384, P-512 |
|
|
|
type kexX25519 struct{} // Used by X25519 |
|
|
|
type kexSidhP503 struct{} // Used by SIDH/P503 |
|
|
|
type kexHybridSidhP503X25519 struct{} // Used by SIDH-ECDH hybrid scheme |
|
|
|
|
|
|
|
// Routing map for key exchange strategies |
|
|
|
var dhKexStrat = map[CurveID]dhKex{ |
|
|
|
CurveP256: &kexNist{}, |
|
|
|
CurveP384: &kexNist{}, |
|
|
|
CurveP521: &kexNist{}, |
|
|
|
X25519: &kexX25519{}, |
|
|
|
sidhP503: &kexSidhP503{}, |
|
|
|
HybridSidhP503Curve25519: &kexHybridSidhP503X25519{}, |
|
|
|
} |
|
|
|
|
|
|
|
func newKeySchedule13(suite *cipherSuite, config *Config, clientRandom []byte) *keySchedule13 { |
|
|
|
if config.KeyLogWriter == nil { |
|
|
|
clientRandom = nil |
|
|
@@ -80,6 +118,15 @@ func (ks *keySchedule13) setSecret(secret []byte) { |
|
|
|
ks.secret = hkdfExtract(hash, secret, salt) |
|
|
|
} |
|
|
|
|
|
|
|
// Depending on role returns pair of key variant to be used by |
|
|
|
// local and remote process. |
|
|
|
func getSidhKeyVariant(isClient bool) (sidh.KeyVariant, sidh.KeyVariant) { |
|
|
|
if isClient { |
|
|
|
return sidh.KeyVariant_SIDH_A, sidh.KeyVariant_SIDH_B |
|
|
|
} |
|
|
|
return sidh.KeyVariant_SIDH_B, sidh.KeyVariant_SIDH_A |
|
|
|
} |
|
|
|
|
|
|
|
// write appends the data to the transcript hash context. |
|
|
|
func (ks *keySchedule13) write(data []byte) { |
|
|
|
ks.handshakeCtx = nil |
|
|
@@ -186,7 +233,7 @@ CurvePreferenceLoop: |
|
|
|
|
|
|
|
earlyClientCipher, _ := hs.keySchedule.prepareCipher(secretEarlyClient) |
|
|
|
|
|
|
|
ecdheSecret := c.deriveECDHESecret(ks, privateKey) |
|
|
|
ecdheSecret := c.deriveDHESecret(ks, privateKey) |
|
|
|
if ecdheSecret == nil { |
|
|
|
c.sendAlert(alertIllegalParameter) |
|
|
|
return errors.New("tls: bad ECDHE client share") |
|
|
@@ -551,61 +598,22 @@ func prepareDigitallySigned(hash crypto.Hash, context string, data []byte) []byt |
|
|
|
return h.Sum(nil) |
|
|
|
} |
|
|
|
|
|
|
|
// generateKeyShare generates keypair. Private key is returned as first argument, public key |
|
|
|
// is returned in keyShare.data. keyshare.curveID stores ID of the scheme used. |
|
|
|
func (c *Conn) generateKeyShare(curveID CurveID) ([]byte, keyShare, error) { |
|
|
|
if curveID == X25519 { |
|
|
|
var scalar, public [32]byte |
|
|
|
if _, err := io.ReadFull(c.config.rand(), scalar[:]); err != nil { |
|
|
|
return nil, keyShare{}, err |
|
|
|
} |
|
|
|
|
|
|
|
curve25519.ScalarBaseMult(&public, &scalar) |
|
|
|
return scalar[:], keyShare{group: curveID, data: public[:]}, nil |
|
|
|
} |
|
|
|
|
|
|
|
curve, ok := curveForCurveID(curveID) |
|
|
|
if !ok { |
|
|
|
return nil, keyShare{}, errors.New("tls: preferredCurves includes unsupported curve") |
|
|
|
if val, ok := dhKexStrat[curveID]; ok { |
|
|
|
return val.generate(c, curveID) |
|
|
|
} |
|
|
|
|
|
|
|
privateKey, x, y, err := elliptic.GenerateKey(curve, c.config.rand()) |
|
|
|
if err != nil { |
|
|
|
return nil, keyShare{}, err |
|
|
|
} |
|
|
|
ecdhePublic := elliptic.Marshal(curve, x, y) |
|
|
|
|
|
|
|
return privateKey, keyShare{group: curveID, data: ecdhePublic}, nil |
|
|
|
return nil, keyShare{}, errors.New("tls: preferredCurves includes unsupported curve") |
|
|
|
} |
|
|
|
|
|
|
|
func (c *Conn) deriveECDHESecret(ks keyShare, secretKey []byte) []byte { |
|
|
|
if ks.group == X25519 { |
|
|
|
if len(ks.data) != 32 { |
|
|
|
return nil |
|
|
|
} |
|
|
|
|
|
|
|
var theirPublic, sharedKey, scalar [32]byte |
|
|
|
copy(theirPublic[:], ks.data) |
|
|
|
copy(scalar[:], secretKey) |
|
|
|
curve25519.ScalarMult(&sharedKey, &scalar, &theirPublic) |
|
|
|
return sharedKey[:] |
|
|
|
} |
|
|
|
|
|
|
|
curve, ok := curveForCurveID(ks.group) |
|
|
|
if !ok { |
|
|
|
return nil |
|
|
|
} |
|
|
|
x, y := elliptic.Unmarshal(curve, ks.data) |
|
|
|
if x == nil { |
|
|
|
return nil |
|
|
|
// DH key agreement. ks stores public key, secretKey stores private key used for ephemeral |
|
|
|
// key agreement. Function returns shared secret in case of success or empty slice otherwise. |
|
|
|
func (c *Conn) deriveDHESecret(ks keyShare, secretKey []byte) []byte { |
|
|
|
if val, ok := dhKexStrat[ks.group]; ok { |
|
|
|
return val.derive(c, ks, secretKey) |
|
|
|
} |
|
|
|
x, _ = curve.ScalarMult(x, y, secretKey) |
|
|
|
xBytes := x.Bytes() |
|
|
|
curveSize := (curve.Params().BitSize + 8 - 1) >> 3 |
|
|
|
if len(xBytes) == curveSize { |
|
|
|
return xBytes |
|
|
|
} |
|
|
|
buf := make([]byte, curveSize) |
|
|
|
copy(buf[len(buf)-len(xBytes):], xBytes) |
|
|
|
return buf |
|
|
|
return nil |
|
|
|
} |
|
|
|
|
|
|
|
func hkdfExpandLabel(hash crypto.Hash, secret, hashValue []byte, label string, L int) []byte { |
|
|
@@ -981,7 +989,7 @@ func (hs *clientHandshakeState) doTLS13Handshake() error { |
|
|
|
|
|
|
|
// 0-RTT is not supported yet, so use an empty PSK. |
|
|
|
hs.keySchedule.setSecret(nil) |
|
|
|
ecdheSecret := c.deriveECDHESecret(serverHello.keyShare, hs.privateKey) |
|
|
|
ecdheSecret := c.deriveDHESecret(serverHello.keyShare, hs.privateKey) |
|
|
|
if ecdheSecret == nil { |
|
|
|
c.sendAlert(alertIllegalParameter) |
|
|
|
return errors.New("tls: bad ECDHE server share") |
|
|
@@ -1161,3 +1169,138 @@ func supportedSigAlgorithmsCert(schemes []SignatureScheme) (ret []SignatureSchem |
|
|
|
} |
|
|
|
return |
|
|
|
} |
|
|
|
|
|
|
|
// Functions below implement dhKex interface for different DH shared secret agreements |
|
|
|
|
|
|
|
// KEX: P-256, P-384, P-512 KEX |
|
|
|
func (kexNist) generate(c *Conn, groupId CurveID) (private []byte, ks keyShare, err error) { |
|
|
|
// never fails |
|
|
|
curve, _ := curveForCurveID(groupId) |
|
|
|
private, x, y, err := elliptic.GenerateKey(curve, c.config.rand()) |
|
|
|
if err != nil { |
|
|
|
return nil, keyShare{}, err |
|
|
|
} |
|
|
|
ks.group = groupId |
|
|
|
ks.data = elliptic.Marshal(curve, x, y) |
|
|
|
return |
|
|
|
} |
|
|
|
func (kexNist) derive(c *Conn, ks keyShare, secretKey []byte) []byte { |
|
|
|
// never fails |
|
|
|
curve, _ := curveForCurveID(ks.group) |
|
|
|
x, y := elliptic.Unmarshal(curve, ks.data) |
|
|
|
if x == nil { |
|
|
|
return nil |
|
|
|
} |
|
|
|
x, _ = curve.ScalarMult(x, y, secretKey) |
|
|
|
xBytes := x.Bytes() |
|
|
|
curveSize := (curve.Params().BitSize + 8 - 1) >> 3 |
|
|
|
if len(xBytes) == curveSize { |
|
|
|
return xBytes |
|
|
|
} |
|
|
|
buf := make([]byte, curveSize) |
|
|
|
copy(buf[len(buf)-len(xBytes):], xBytes) |
|
|
|
return buf |
|
|
|
} |
|
|
|
|
|
|
|
// KEX: X25519 |
|
|
|
func (kexX25519) generate(c *Conn, groupId CurveID) ([]byte, keyShare, error) { |
|
|
|
var scalar, public [x25519SharedSecretSz]byte |
|
|
|
if _, err := io.ReadFull(c.config.rand(), scalar[:]); err != nil { |
|
|
|
return nil, keyShare{}, err |
|
|
|
} |
|
|
|
curve25519.ScalarBaseMult(&public, &scalar) |
|
|
|
return scalar[:], keyShare{group: X25519, data: public[:]}, nil |
|
|
|
} |
|
|
|
|
|
|
|
func (kexX25519) derive(c *Conn, ks keyShare, secretKey []byte) []byte { |
|
|
|
var theirPublic, sharedKey, scalar [x25519SharedSecretSz]byte |
|
|
|
if len(ks.data) != x25519SharedSecretSz { |
|
|
|
return nil |
|
|
|
} |
|
|
|
copy(theirPublic[:], ks.data) |
|
|
|
copy(scalar[:], secretKey) |
|
|
|
curve25519.ScalarMult(&sharedKey, &scalar, &theirPublic) |
|
|
|
return sharedKey[:] |
|
|
|
} |
|
|
|
|
|
|
|
// KEX: SIDH/503 |
|
|
|
func (kexSidhP503) generate(c *Conn, groupId CurveID) ([]byte, keyShare, error) { |
|
|
|
var variant, _ = getSidhKeyVariant(c.isClient) |
|
|
|
var prvKey = sidh.NewPrivateKey(sidh.FP_503, variant) |
|
|
|
if prvKey.Generate(c.config.rand()) != nil { |
|
|
|
return nil, keyShare{}, errors.New("tls: private SIDH key generation failed") |
|
|
|
} |
|
|
|
pubKey := prvKey.GeneratePublicKey() |
|
|
|
return prvKey.Export(), keyShare{group: sidhP503, data: pubKey.Export()}, nil |
|
|
|
} |
|
|
|
|
|
|
|
func (kexSidhP503) derive(c *Conn, ks keyShare, key []byte) []byte { |
|
|
|
var prvVariant, pubVariant = getSidhKeyVariant(c.isClient) |
|
|
|
var prvKeySize = P503PrvKeySz |
|
|
|
|
|
|
|
if len(ks.data) != P503PubKeySz || len(key) != prvKeySize { |
|
|
|
return nil |
|
|
|
} |
|
|
|
|
|
|
|
prvKey := sidh.NewPrivateKey(sidh.FP_503, prvVariant) |
|
|
|
pubKey := sidh.NewPublicKey(sidh.FP_503, pubVariant) |
|
|
|
|
|
|
|
if err := prvKey.Import(key); err != nil { |
|
|
|
return nil |
|
|
|
} |
|
|
|
if err := pubKey.Import(ks.data); err != nil { |
|
|
|
return nil |
|
|
|
} |
|
|
|
|
|
|
|
// Never fails |
|
|
|
sharedKey, _ := sidh.DeriveSecret(prvKey, pubKey) |
|
|
|
return sharedKey |
|
|
|
} |
|
|
|
|
|
|
|
// KEX Hybrid SIDH/503-X25519 |
|
|
|
func (kexHybridSidhP503X25519) generate(c *Conn, groupId CurveID) (private []byte, ks keyShare, err error) { |
|
|
|
var pubHybrid [SidhP503Curve25519PubKeySz]byte |
|
|
|
var prvHybrid [SidhP503Curve25519PrvKeySz]byte |
|
|
|
|
|
|
|
// Generate ephemeral key for classic x25519 |
|
|
|
private, ks, err = dhKexStrat[X25519].generate(c, groupId) |
|
|
|
if err != nil { |
|
|
|
return |
|
|
|
} |
|
|
|
copy(prvHybrid[:], private) |
|
|
|
copy(pubHybrid[:], ks.data) |
|
|
|
|
|
|
|
// Generate PQ ephemeral key for SIDH |
|
|
|
private, ks, err = dhKexStrat[sidhP503].generate(c, groupId) |
|
|
|
if err != nil { |
|
|
|
return |
|
|
|
} |
|
|
|
copy(prvHybrid[x25519SharedSecretSz:], private) |
|
|
|
copy(pubHybrid[x25519SharedSecretSz:], ks.data) |
|
|
|
return prvHybrid[:], keyShare{group: HybridSidhP503Curve25519, data: pubHybrid[:]}, nil |
|
|
|
} |
|
|
|
|
|
|
|
func (kexHybridSidhP503X25519) derive(c *Conn, ks keyShare, key []byte) []byte { |
|
|
|
var sharedKey [SidhP503Curve25519SharedKeySz]byte |
|
|
|
var ret []byte |
|
|
|
var tmpKs keyShare |
|
|
|
|
|
|
|
// Key agreement for classic |
|
|
|
tmpKs.group = X25519 |
|
|
|
tmpKs.data = ks.data[:x25519SharedSecretSz] |
|
|
|
ret = dhKexStrat[X25519].derive(c, tmpKs, key[:x25519SharedSecretSz]) |
|
|
|
if ret == nil { |
|
|
|
return nil |
|
|
|
} |
|
|
|
copy(sharedKey[:], ret) |
|
|
|
|
|
|
|
// Key agreement for PQ |
|
|
|
tmpKs.group = sidhP503 |
|
|
|
tmpKs.data = ks.data[x25519SharedSecretSz:] |
|
|
|
ret = dhKexStrat[sidhP503].derive(c, tmpKs, key[x25519SharedSecretSz:]) |
|
|
|
if ret == nil { |
|
|
|
return nil |
|
|
|
} |
|
|
|
copy(sharedKey[x25519SharedSecretSz:], ret) |
|
|
|
return sharedKey[:] |
|
|
|
} |