Skip to content

Commit e47a4e7

Browse files
committed
tris: experimental X25519+SIDH hybrid key agreement
This uses the Go SIDH implementation to do a hybrid X25519+SIDH key agreement. The SIDHP751AndX25519 key agreement (code 0xFE24, in the private use block) uses the first 32 bytes of the keyshare to do X25519 and the remaining 564 bytes to do an SIDH P-751 key agreement. The shared secrets are concatenated together and fed into the TLS key derivation mechanism. At the moment this branch won't build without manually vendoring the Go SIDH implementation into the custom GOROOT.
1 parent 9b08dfa commit e47a4e7

4 files changed

Lines changed: 100 additions & 17 deletions

File tree

13.go

Lines changed: 91 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,22 @@ import (
2323
"sync/atomic"
2424
"time"
2525

26+
"github_com/cloudflare/p751sidh"
2627
"golang_org/x/crypto/curve25519"
2728
)
2829

2930
// numSessionTickets is the number of different session tickets the
3031
// server sends to a TLS 1.3 client, who will use each only once.
3132
const numSessionTickets = 2
3233

34+
// In SIDH, the computations done by Alice are different from Bob's.
35+
type dhRole bool
36+
37+
const (
38+
dhRoleServer dhRole = true
39+
dhRoleClient dhRole = false
40+
)
41+
3342
func (hs *serverHandshakeState) doTLS13Handshake() error {
3443
config := hs.c.config
3544
c := hs.c
@@ -61,7 +70,7 @@ CurvePreferenceLoop:
6170
}
6271
}
6372

64-
privateKey, serverKS, err := config.generateKeyShare(ks.group)
73+
privateKey, serverKS, err := config.generateKeyShare(ks.group, dhRoleServer)
6574
if err != nil {
6675
c.sendAlert(alertInternalError)
6776
return err
@@ -93,8 +102,8 @@ CurvePreferenceLoop:
93102
handshakeCtx := hs.finishedHash13.Sum(nil)
94103
earlyClientCipher, _ := hs.suite.prepareCipher(handshakeCtx, earlySecret, "client early traffic secret")
95104

96-
ecdheSecret := deriveECDHESecret(ks, privateKey)
97-
if ecdheSecret == nil {
105+
dheSecret := deriveDHESecret(ks, privateKey, dhRoleServer)
106+
if dheSecret == nil {
98107
c.sendAlert(alertIllegalParameter)
99108
return errors.New("tls: bad ECDHE client share")
100109
}
@@ -104,7 +113,7 @@ CurvePreferenceLoop:
104113
return err
105114
}
106115

107-
handshakeSecret := hkdfExtract(hash, ecdheSecret, earlySecret)
116+
handshakeSecret := hkdfExtract(hash, dheSecret, earlySecret)
108117
handshakeCtx = hs.finishedHash13.Sum(nil)
109118
clientCipher, cTrafficSecret := hs.suite.prepareCipher(handshakeCtx, handshakeSecret, "client handshake traffic secret")
110119
hs.hsClientCipher = clientCipher
@@ -367,7 +376,38 @@ func prepareDigitallySigned(hash crypto.Hash, context string, data []byte) []byt
367376
return h.Sum(nil)
368377
}
369378

370-
func (c *Config) generateKeyShare(curveID CurveID) ([]byte, keyShare, error) {
379+
func (c *Config) generateKeyShare(curveID CurveID, role dhRole) ([]byte, keyShare, error) {
380+
if curveID == SIDHP751AndX25519 {
381+
var scalarX25519, publicX25519 [32]byte
382+
if _, err := io.ReadFull(c.rand(), scalarX25519[:]); err != nil {
383+
return nil, keyShare{}, err
384+
}
385+
curve25519.ScalarBaseMult(&publicX25519, &scalarX25519)
386+
387+
var secret [32 + p751sidh.SecretKeySize]byte
388+
var public [32 + p751sidh.PublicKeySize]byte
389+
copy(secret[:32], scalarX25519[:])
390+
copy(public[:32], publicX25519[:])
391+
392+
// Alice's computations are slightly cheaper, so we use them on the server.
393+
if role == dhRoleServer {
394+
var publicSIDH, secretSIDH, err = p751sidh.GenerateAliceKeypair(c.rand())
395+
if err != nil {
396+
return nil, keyShare{}, err
397+
}
398+
copy(secret[32:], secretSIDH.Scalar[:])
399+
publicSIDH.ToBytes(public[32:])
400+
} else {
401+
var publicSIDH, secretSIDH, err = p751sidh.GenerateBobKeypair(c.rand())
402+
if err != nil {
403+
return nil, keyShare{}, err
404+
}
405+
copy(secret[32:], secretSIDH.Scalar[:])
406+
publicSIDH.ToBytes(public[32:])
407+
}
408+
409+
return secret[:], keyShare{group: curveID, data: public[:]}, nil
410+
}
371411
if curveID == X25519 {
372412
var scalar, public [32]byte
373413
if _, err := io.ReadFull(c.rand(), scalar[:]); err != nil {
@@ -392,7 +432,48 @@ func (c *Config) generateKeyShare(curveID CurveID) ([]byte, keyShare, error) {
392432
return privateKey, keyShare{group: curveID, data: ecdhePublic}, nil
393433
}
394434

395-
func deriveECDHESecret(ks keyShare, secretKey []byte) []byte {
435+
func deriveDHESecret(ks keyShare, secretKey []byte, role dhRole) []byte {
436+
if ks.group == SIDHP751AndX25519 {
437+
if len(ks.data) != 32+p751sidh.PublicKeySize {
438+
return nil
439+
}
440+
if len(secretKey) != 32+p751sidh.SecretKeySize {
441+
return nil
442+
}
443+
444+
var theirX25519Public, sharedX25519Secret, ourX25519Secret [32]byte
445+
copy(theirX25519Public[:], ks.data[:32])
446+
copy(ourX25519Secret[:], secretKey[:32])
447+
curve25519.ScalarMult(&sharedX25519Secret, &ourX25519Secret, &theirX25519Public)
448+
449+
var sharedSIDHSecret [p751sidh.SharedSecretSize]byte
450+
451+
// Alice's computations are slightly cheaper, so we use them on the server.
452+
if role == dhRoleServer {
453+
var theirSIDHPublic p751sidh.SIDHPublicKeyBob
454+
theirSIDHPublic.FromBytes(ks.data[32:])
455+
456+
var ourSIDHSecret p751sidh.SIDHSecretKeyAlice
457+
copy(ourSIDHSecret.Scalar[:], secretKey[32:])
458+
459+
sharedSIDHSecret = ourSIDHSecret.SharedSecret(&theirSIDHPublic)
460+
} else {
461+
var theirSIDHPublic p751sidh.SIDHPublicKeyAlice
462+
theirSIDHPublic.FromBytes(ks.data[32:])
463+
464+
var ourSIDHSecret p751sidh.SIDHSecretKeyBob
465+
copy(ourSIDHSecret.Scalar[:], secretKey[32:])
466+
467+
sharedSIDHSecret = ourSIDHSecret.SharedSecret(&theirSIDHPublic)
468+
}
469+
470+
var sharedSecret [32 + p751sidh.SharedSecretSize]byte
471+
copy(sharedSecret[:32], sharedX25519Secret[:])
472+
copy(sharedSecret[32:], sharedSIDHSecret[:])
473+
474+
return sharedSecret[:]
475+
}
476+
396477
if ks.group == X25519 {
397478
if len(ks.data) != 32 {
398479
return nil
@@ -730,7 +811,7 @@ func (hs *clientHandshakeState) startTLS13ClientHandshake() error {
730811
// forces handling the case of an extra round trip
731812

732813
var preferredCurve = hello.supportedCurves[0]
733-
privateKey, clientKeyShare, err := config.generateKeyShare(preferredCurve)
814+
privateKey, clientKeyShare, err := config.generateKeyShare(preferredCurve, dhRoleClient)
734815
if err != nil {
735816
return err
736817
}
@@ -783,15 +864,15 @@ func (hs *clientHandshakeState) doTLS13ClientHandshake() error {
783864
earlySecret := hkdfExtract(hash, nil, nil)
784865

785866
serverKeyShare := hs.serverHello13.keyShare
786-
ecdheSecret := deriveECDHESecret(serverKeyShare, hs.keySharePrivateKey)
787-
if ecdheSecret == nil {
867+
dheSecret := deriveDHESecret(serverKeyShare, hs.keySharePrivateKey, dhRoleClient)
868+
if dheSecret == nil {
788869
c.sendAlert(alertIllegalParameter)
789870
return errors.New("tls: bad ECDHE client share")
790871
}
791872

792873
hs.finishedHash13.Write(hs.serverHello13.marshal())
793874

794-
handshakeSecret := hkdfExtract(hash, ecdheSecret, earlySecret)
875+
handshakeSecret := hkdfExtract(hash, dheSecret, earlySecret)
795876
handshakeCtx := hs.finishedHash13.Sum(nil)
796877
clientCipher, cTrafficSecret := suite.prepareCipher(handshakeCtx, handshakeSecret, "client handshake traffic secret")
797878
serverCipher, sTrafficSecret := suite.prepareCipher(handshakeCtx, handshakeSecret, "server handshake traffic secret")

_dev/tris-client/client.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ func main() {
1818

1919
config := &tls.Config{
2020
MaxVersion: tls.VersionTLS13Draft18,
21-
//MaxVersion: tls.VersionTLS12,
22-
//InsecureSkipVerify: true,
21+
CurvePreferences: []tls.CurveID{tls.SIDHP751AndX25519},
22+
InsecureSkipVerify: true,
2323
}
2424

2525
trans := &http.Transport{TLSClientConfig: config}
@@ -29,7 +29,7 @@ func main() {
2929
if err != nil {
3030
panic(err)
3131
}
32-
32+
3333
fmt.Println("RESPONSE:")
3434
fmt.Println("%v", resp)
3535
}

_dev/tris-localserver/server.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ func startServer(addr string, rsa, offer0RTT, accept0RTT, shortHdr bool) {
3434
s := &http.Server{
3535
Addr: addr,
3636
TLSConfig: &tls.Config{
37+
CurvePreferences: []tls.CurveID{tls.SIDHP751AndX25519, tls.X25519},
3738
Certificates: []tls.Certificate{cert},
3839
Max0RTTDataSize: Max0RTTDataSize,
3940
Accept0RTTData: accept0RTT,

common.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -114,10 +114,11 @@ const (
114114
type CurveID uint16
115115

116116
const (
117-
CurveP256 CurveID = 23
118-
CurveP384 CurveID = 24
119-
CurveP521 CurveID = 25
120-
X25519 CurveID = 29
117+
CurveP256 CurveID = 23
118+
CurveP384 CurveID = 24
119+
CurveP521 CurveID = 25
120+
X25519 CurveID = 29
121+
SIDHP751AndX25519 CurveID = 0xFE24 // 0xFE00-0xFEFF private use block; 24 is magic
121122
)
122123

123124
// TLS 1.3 Key Share

0 commit comments

Comments
 (0)