Adds X25519-SIKEp503 key agreement (#159)
* removes useless variable * [sike] Adds X25519-SIKEp503 with cSHAKE
This commit is contained in:
vanhempi
a5d35123cc
commit
600d4dece5
201
13.go
201
13.go
@ -22,6 +22,7 @@ import (
|
||||
"time"
|
||||
|
||||
sidh "github_com/cloudflare/sidh/sidh"
|
||||
sike "github_com/cloudflare/sidh/sike"
|
||||
"golang_org/x/crypto/curve25519"
|
||||
)
|
||||
|
||||
@ -32,14 +33,22 @@ const numSessionTickets = 2
|
||||
type secretLabel int
|
||||
|
||||
const (
|
||||
x25519SharedSecretSz = 32
|
||||
// Both public key and shared secret size
|
||||
x25519Sz = 32
|
||||
|
||||
P503PubKeySz = 378
|
||||
P503PrvKeySz = 32
|
||||
P503SharedSecretSz = 126
|
||||
SIDHp503Curve25519PubKeySz = x25519SharedSecretSz + P503PubKeySz
|
||||
SIDHp503Curve25519PrvKeySz = x25519SharedSecretSz + P503PrvKeySz
|
||||
SIDHp503Curve25519SharedKeySz = x25519SharedSecretSz + P503SharedSecretSz
|
||||
SIDHp503PubKeySz = 378
|
||||
SIDHp503PrvKeySz = 32
|
||||
SIDHp503SharedSecretSz = 126
|
||||
SIDHp503Curve25519PubKeySz = x25519Sz + SIDHp503PubKeySz
|
||||
SIDHp503Curve25519PrvKeySz = x25519Sz + SIDHp503PrvKeySz
|
||||
SIDHp503Curve25519SharedKeySz = x25519Sz + SIDHp503SharedSecretSz
|
||||
|
||||
SIKEp503SharedSecretSz = 16
|
||||
SIKEp503CtSz = SIDHp503PubKeySz + 24
|
||||
SIKEp503Curve25519CtSz = x25519Sz + SIKEp503CtSz
|
||||
SIKEp503Curve25519PubKeySz = x25519Sz + SIDHp503PubKeySz
|
||||
SIKEp503Curve25519PrvKeySz = x25519Sz + SIDHp503PrvKeySz + 24
|
||||
SIKEp503Curve25519SharedKeySz = x25519Sz + SIKEp503SharedSecretSz
|
||||
)
|
||||
|
||||
const (
|
||||
@ -111,12 +120,18 @@ func (defaultServerKEX) keyAgreementServer(c *Conn, clientKS keyShare) ([]byte,
|
||||
type kexNIST struct{ defaultServerKEX } // Used by NIST curves; P-256, P-384, P-512
|
||||
type kexX25519 struct{ defaultServerKEX } // Used by X25519
|
||||
type kexSIDHp503 struct{ defaultServerKEX } // Used by SIDH/P503
|
||||
type kexSIKEp503 struct{} // Used by SIKE/P503
|
||||
type kexHybridSIDHp503X25519 struct {
|
||||
defaultServerKEX
|
||||
classicKEX kexX25519
|
||||
pqKEX kexSIDHp503
|
||||
} // Used by SIDH-ECDH hybrid scheme
|
||||
|
||||
type kexHybridSIKEp503X25519 struct {
|
||||
classicKEX kexX25519
|
||||
pqKEX kexSIKEp503
|
||||
} // Used by SIKE-ECDHE hybrid scheme
|
||||
|
||||
// Routing map for key exchange strategies
|
||||
var kexStrat = map[CurveID]kex{
|
||||
CurveP256: &kexNIST{},
|
||||
@ -124,6 +139,7 @@ var kexStrat = map[CurveID]kex{
|
||||
CurveP521: &kexNIST{},
|
||||
X25519: &kexX25519{},
|
||||
HybridSIDHp503Curve25519: &kexHybridSIDHp503X25519{},
|
||||
HybridSIKEp503Curve25519: &kexHybridSIKEp503X25519{},
|
||||
}
|
||||
|
||||
func newKeySchedule13(suite *cipherSuite, config *Config, clientRandom []byte) *keySchedule13 {
|
||||
@ -1238,7 +1254,7 @@ func (kexNIST) keyAgreementClient(c *Conn, ks keyShare, secretKey []byte) ([]byt
|
||||
|
||||
// KEX: X25519
|
||||
func (kexX25519) generate(c *Conn, groupId CurveID) ([]byte, keyShare, error) {
|
||||
var scalar, public [x25519SharedSecretSz]byte
|
||||
var scalar, public [x25519Sz]byte
|
||||
if _, err := io.ReadFull(c.config.rand(), scalar[:]); err != nil {
|
||||
return nil, keyShare{}, err
|
||||
}
|
||||
@ -1247,8 +1263,8 @@ func (kexX25519) generate(c *Conn, groupId CurveID) ([]byte, keyShare, error) {
|
||||
}
|
||||
|
||||
func (kexX25519) keyAgreementClient(c *Conn, ks keyShare, secretKey []byte) ([]byte, error) {
|
||||
var theirPublic, sharedKey, scalar [x25519SharedSecretSz]byte
|
||||
if len(ks.data) != x25519SharedSecretSz {
|
||||
var theirPublic, sharedKey, scalar [x25519Sz]byte
|
||||
if len(ks.data) != x25519Sz {
|
||||
return nil, errors.New("tls: wrong shared secret size")
|
||||
}
|
||||
copy(theirPublic[:], ks.data)
|
||||
@ -1270,9 +1286,8 @@ func (kexSIDHp503) generate(c *Conn, groupId CurveID) ([]byte, keyShare, error)
|
||||
|
||||
func (kexSIDHp503) keyAgreementClient(c *Conn, ks keyShare, key []byte) ([]byte, error) {
|
||||
var prvVariant, pubVariant = getSidhKeyVariant(c.isClient)
|
||||
var prvKeySize = P503PrvKeySz
|
||||
|
||||
if len(ks.data) != P503PubKeySz || len(key) != prvKeySize {
|
||||
if len(ks.data) != SIDHp503PubKeySz || len(key) != SIDHp503PrvKeySz {
|
||||
return nil, errors.New("tls: wrong key size")
|
||||
}
|
||||
|
||||
@ -1309,20 +1324,20 @@ func (kex *kexHybridSIDHp503X25519) generate(c *Conn, groupId CurveID) (private
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
copy(prvHybrid[x25519SharedSecretSz:], private)
|
||||
copy(pubHybrid[x25519SharedSecretSz:], ks.data)
|
||||
copy(prvHybrid[x25519Sz:], private)
|
||||
copy(pubHybrid[x25519Sz:], ks.data)
|
||||
return prvHybrid[:], keyShare{group: HybridSIDHp503Curve25519, data: pubHybrid[:]}, nil
|
||||
}
|
||||
|
||||
func (kex *kexHybridSIDHp503X25519) keyAgreementClient(c *Conn, ks keyShare, key []byte) ([]byte, error) {
|
||||
func (kex *kexHybridSIDHp503X25519) keyAgreementClient(c *Conn, theirsKS keyShare, key []byte) ([]byte, error) {
|
||||
var sharedKey [SIDHp503Curve25519SharedKeySz]byte
|
||||
var ret []byte
|
||||
var tmpKs keyShare
|
||||
|
||||
// Key agreement for classic
|
||||
tmpKs.group = X25519
|
||||
tmpKs.data = ks.data[:x25519SharedSecretSz]
|
||||
ret, err := kex.classicKEX.keyAgreementClient(c, tmpKs, key[:x25519SharedSecretSz])
|
||||
tmpKs.data = theirsKS.data[:x25519Sz]
|
||||
ret, err := kex.classicKEX.keyAgreementClient(c, tmpKs, key[:x25519Sz])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -1330,11 +1345,157 @@ func (kex *kexHybridSIDHp503X25519) keyAgreementClient(c *Conn, ks keyShare, key
|
||||
|
||||
// Key agreement for PQ
|
||||
tmpKs.group = 0 /*UNUSED*/
|
||||
tmpKs.data = ks.data[x25519SharedSecretSz:]
|
||||
ret, err = kex.pqKEX.keyAgreementClient(c, tmpKs, key[x25519SharedSecretSz:])
|
||||
tmpKs.data = theirsKS.data[x25519Sz:]
|
||||
ret, err = kex.pqKEX.keyAgreementClient(c, tmpKs, key[x25519Sz:])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
copy(sharedKey[x25519SharedSecretSz:], ret)
|
||||
copy(sharedKey[x25519Sz:], ret)
|
||||
return sharedKey[:], nil
|
||||
}
|
||||
|
||||
// generate method generates SIKE key pair (ephemeral) on client side
|
||||
func (kexSIKEp503) generate(c *Conn, groupId CurveID) ([]byte, keyShare, error) {
|
||||
if !c.isClient {
|
||||
return nil, keyShare{}, errors.New("tls: internal error")
|
||||
}
|
||||
|
||||
var prvKey = sidh.NewPrivateKey(sidh.FP_503, sidh.KeyVariant_SIKE)
|
||||
if prvKey.Generate(c.config.rand()) != nil {
|
||||
return nil, keyShare{}, errors.New("tls: private SIDH key generation failed")
|
||||
}
|
||||
var pubKey = prvKey.GeneratePublicKey()
|
||||
var ks = keyShare{data: pubKey.Export()}
|
||||
|
||||
// 'buf' is a concatenation of (private || public) key. I need public key
|
||||
// when decapsulating in kexSIKEp503::keyAgreementClient.
|
||||
var buf = make([]byte, prvKey.Size()+pubKey.Size())
|
||||
copy(buf, prvKey.Export())
|
||||
copy(buf[prvKey.Size():], ks.data)
|
||||
return buf, ks, nil
|
||||
}
|
||||
|
||||
// keyAgreementClient performs KEM decapsulation. 'privateKey' is a concatenation
|
||||
// of (private || public) key
|
||||
func (kexSIKEp503) keyAgreementClient(c *Conn, theirsKS keyShare, privateKey []byte) ([]byte, error) {
|
||||
// Import private key
|
||||
var prvKey = sidh.NewPrivateKey(sidh.FP_503, sidh.KeyVariant_SIKE)
|
||||
var pubKey = sidh.NewPublicKey(sidh.FP_503, sidh.KeyVariant_SIKE)
|
||||
|
||||
if len(privateKey) != prvKey.Size()+pubKey.Size() {
|
||||
return nil, errors.New("tls: internal error")
|
||||
}
|
||||
|
||||
// Never fails
|
||||
prvKey.Import(privateKey[:prvKey.Size()])
|
||||
pubKey.Import(privateKey[prvKey.Size():])
|
||||
|
||||
ss, err := sike.Decapsulate(prvKey, pubKey, theirsKS.data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ss, nil
|
||||
}
|
||||
|
||||
// keyAgreementServer performs KEM encapsulation.
|
||||
func (kexSIKEp503) keyAgreementServer(c *Conn, theirsKS keyShare) ([]byte, keyShare, error) {
|
||||
pubKey := sidh.NewPublicKey(sidh.FP_503, sidh.KeyVariant_SIKE)
|
||||
if pubKey.Import(theirsKS.data) != nil {
|
||||
return nil, keyShare{}, errors.New("tls: can't import public SIKE key")
|
||||
}
|
||||
ct, key, err := sike.Encapsulate(c.config.rand(), pubKey)
|
||||
if err != nil {
|
||||
return nil, keyShare{}, errors.New("tls: SIKE encapsulation failed")
|
||||
}
|
||||
return key, keyShare{data: ct}, nil
|
||||
}
|
||||
|
||||
// KEX Hybrid SIKEp503-X25519
|
||||
func (kex *kexHybridSIKEp503X25519) generate(c *Conn, groupId CurveID) ([]byte, keyShare, error) {
|
||||
var pubHybrid [SIKEp503Curve25519PubKeySz]byte
|
||||
var prvHybrid [SIKEp503Curve25519PrvKeySz + SIDHp503PubKeySz]byte
|
||||
|
||||
// Generate ephemeral key for classic x25519
|
||||
private, ks, err := kex.classicKEX.generate(c, 0)
|
||||
if err != nil {
|
||||
return nil, keyShare{}, err
|
||||
}
|
||||
copy(prvHybrid[:], private)
|
||||
copy(pubHybrid[:], ks.data)
|
||||
|
||||
// Generate PQ ephemeral key for SIDH
|
||||
private, ks, err = kex.pqKEX.generate(c, 0)
|
||||
if err != nil {
|
||||
return nil, keyShare{}, err
|
||||
}
|
||||
copy(prvHybrid[x25519Sz:], private)
|
||||
copy(pubHybrid[x25519Sz:], ks.data)
|
||||
return prvHybrid[:], keyShare{group: HybridSIKEp503Curve25519, data: pubHybrid[:]}, nil
|
||||
}
|
||||
|
||||
// keyAgreementClient performs X25519-SIKEp503 key agreement on client side. 'theirsKS.data' contains
|
||||
// X25519 public key and SIKEp503 KEM generated by the server. 'privateKey' is a key stored
|
||||
// locally by the process. It is a concatenation of (X25519 || SIKEp503 private || SIKEp503 public) keys.
|
||||
// In case of success concatenation of (X25519||SIKEp503) shared secrets is returned (32+16 bytes).
|
||||
func (kex *kexHybridSIKEp503X25519) keyAgreementClient(c *Conn, theirsKS keyShare, privateKey []byte) ([]byte, error) {
|
||||
var ssHyb [SIKEp503Curve25519SharedKeySz]byte
|
||||
var tmpKs keyShare
|
||||
|
||||
if len(privateKey) != SIKEp503Curve25519PrvKeySz+SIDHp503PubKeySz {
|
||||
return nil, errors.New("tls: internal error")
|
||||
}
|
||||
|
||||
if len(theirsKS.data) != SIKEp503Curve25519CtSz {
|
||||
return nil, errors.New("tls: wrong key size for X25519-SIKEp503")
|
||||
}
|
||||
|
||||
// Key agreement for classic
|
||||
tmpKs.group = X25519
|
||||
tmpKs.data = theirsKS.data[:x25519Sz]
|
||||
ret, err := kex.classicKEX.keyAgreementClient(c, tmpKs, privateKey[:x25519Sz])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
copy(ssHyb[:], ret)
|
||||
|
||||
// Key agreement for PQ
|
||||
tmpKs.group = 0 /*UNUSED*/
|
||||
tmpKs.data = theirsKS.data[x25519Sz:]
|
||||
ret, err = kex.pqKEX.keyAgreementClient(c, tmpKs, privateKey[x25519Sz:])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
copy(ssHyb[x25519Sz:], ret[:])
|
||||
return ssHyb[:], nil
|
||||
}
|
||||
|
||||
// keyAgreementServer performs X25519-SIKEp503 shared secret agreement on a server side. 'theirsKS'
|
||||
// contains concatenation of public keys for both X25519 and SIKEp503. In case of success
|
||||
// function returns X25519 and SIKEp503 shaerd secret concatenated together and concatenation of
|
||||
// X25519 public and SIKEp503 ciphertext that are sent to the client.
|
||||
func (kex *kexHybridSIKEp503X25519) keyAgreementServer(c *Conn, theirsKS keyShare) ([]byte, keyShare, error) {
|
||||
var ssHyb [SIKEp503Curve25519SharedKeySz]byte
|
||||
var ret [SIKEp503Curve25519CtSz]byte
|
||||
|
||||
if len(theirsKS.data) != SIKEp503Curve25519PubKeySz {
|
||||
return nil, keyShare{}, errors.New("tls: wrong key size for X25519-SIKEp503")
|
||||
}
|
||||
|
||||
var tmpKs = keyShare{group: X25519, data: theirsKS.data[:x25519Sz]}
|
||||
ss, srvKs, err := kex.classicKEX.keyAgreementServer(c, tmpKs)
|
||||
if err != nil {
|
||||
return nil, keyShare{}, err
|
||||
}
|
||||
copy(ssHyb[:], ss[:])
|
||||
copy(ret[:], srvKs.data[:])
|
||||
|
||||
tmpKs.group = 0 /*UNUSED*/
|
||||
tmpKs.data = theirsKS.data[x25519Sz:]
|
||||
ss, srvKs, err = kex.pqKEX.keyAgreementServer(c, tmpKs)
|
||||
if err != nil {
|
||||
return nil, keyShare{}, err
|
||||
}
|
||||
copy(ssHyb[x25519Sz:], ss[:])
|
||||
copy(ret[x25519Sz:], srvKs.data[:SIKEp503CtSz])
|
||||
return ssHyb[:], keyShare{group: HybridSIKEp503Curve25519, data: ret[:]}, nil
|
||||
}
|
||||
|
@ -292,6 +292,10 @@ class InteropServer_TRIS(ClientNominalMixin, InteropServer, unittest.TestCase):
|
||||
res = self.d.run_client(self.CLIENT_NAME, '-rsa=false -ecdsa=true -groups X25519-SIDHp503 '+self.server_ip+":7443")
|
||||
self.assertEqual(res[0], 0)
|
||||
|
||||
def test_SIKE(self):
|
||||
res = self.d.run_client(self.CLIENT_NAME, '-rsa=false -ecdsa=true -groups X25519-SIKEp503 '+self.server_ip+":7443")
|
||||
self.assertEqual(res[0], 0)
|
||||
|
||||
def test_server_doesnt_support_SIDH(self):
|
||||
'''
|
||||
Client advertises HybridSIDH and ECDH. Server supports ECDH only. Checks weather
|
||||
|
@ -6,6 +6,6 @@
|
||||
./tris-localserver -b 0.0.0.0:4443 -cert=ecdsa -rtt0=oa 2>&1 & # fourth port: offer and accept 0-RTT
|
||||
./tris-localserver -b 0.0.0.0:5443 -cert=ecdsa -rtt0=oa -rtt0ack 2>&1 & # fifth port: offer and accept 0-RTT but confirm
|
||||
./tris-localserver -b 0.0.0.0:6443 -cert=rsa -cliauth 2>&1 & # sixth port: RSA with required client authentication
|
||||
./tris-localserver -b 0.0.0.0:7443 -cert=ecdsa -qr=c & # Enables support for both - post-quantum and classical KEX algorithms
|
||||
./tris-localserver -b 0.0.0.0:7443 -cert=ecdsa -pq=c & # Enables support for both - post-quantum and classical KEX algorithms
|
||||
|
||||
wait
|
||||
|
@ -55,13 +55,13 @@ func NewServer() *server {
|
||||
return s
|
||||
}
|
||||
|
||||
func enableQR(s *server, enableDefault bool) {
|
||||
var sidhCurves = []tls.CurveID{tls.HybridSIDHp503Curve25519}
|
||||
func enablePQ(s *server, enableDefault bool) {
|
||||
var pqGroups = []tls.CurveID{tls.HybridSIDHp503Curve25519, tls.HybridSIKEp503Curve25519}
|
||||
if enableDefault {
|
||||
var defaultCurvePreferences = []tls.CurveID{tls.X25519, tls.CurveP256, tls.CurveP384, tls.CurveP521}
|
||||
s.TLS.CurvePreferences = append(s.TLS.CurvePreferences, defaultCurvePreferences...)
|
||||
}
|
||||
s.TLS.CurvePreferences = append(s.TLS.CurvePreferences, sidhCurves...)
|
||||
s.TLS.CurvePreferences = append(s.TLS.CurvePreferences, pqGroups...)
|
||||
}
|
||||
|
||||
func (s *server) start() {
|
||||
@ -153,7 +153,7 @@ func main() {
|
||||
arg_zerortt := flag.String("rtt0", "n", `0-RTT, accepts following values [n: None, a: Accept, o: Offer, oa: Offer and Accept]`)
|
||||
arg_confirm := flag.Bool("rtt0ack", false, "0-RTT confirm")
|
||||
arg_clientauth := flag.Bool("cliauth", false, "Performs client authentication (RequireAndVerifyClientCert used)")
|
||||
arg_qr := flag.String("qr", "", "Enable quantum-resistant algorithms [c: Support classical and Quantum-Resistant, q: Enable Quantum-Resistant only]")
|
||||
arg_pq := flag.String("pq", "", "Enable quantum-resistant algorithms [c: Support classical and Quantum-Resistant, q: Enable Quantum-Resistant only]")
|
||||
flag.Parse()
|
||||
|
||||
s.Address = *arg_addr
|
||||
@ -172,10 +172,10 @@ func main() {
|
||||
s.TLS.ClientAuth = tls.RequireAndVerifyClientCert
|
||||
}
|
||||
|
||||
if *arg_qr == "c" {
|
||||
enableQR(s, true)
|
||||
} else if *arg_qr == "q" {
|
||||
enableQR(s, false)
|
||||
if *arg_pq == "c" {
|
||||
enablePQ(s, true)
|
||||
} else if *arg_pq == "q" {
|
||||
enablePQ(s, false)
|
||||
}
|
||||
|
||||
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
|
@ -29,6 +29,7 @@ var cipherSuiteIdToName = map[uint16]string{
|
||||
|
||||
var namedGroupsToName = map[uint16]string{
|
||||
uint16(tls.HybridSIDHp503Curve25519): "X25519-SIDHp503",
|
||||
uint16(tls.HybridSIKEp503Curve25519): "X25519-SIKEp503",
|
||||
uint16(tls.X25519): "X25519",
|
||||
uint16(tls.CurveP256): "P-256",
|
||||
uint16(tls.CurveP384): "P-384",
|
||||
|
@ -124,6 +124,7 @@ const (
|
||||
|
||||
// Experimental KEX
|
||||
HybridSIDHp503Curve25519 CurveID = 0xFE30
|
||||
HybridSIKEp503Curve25519 CurveID = 0xFE32
|
||||
)
|
||||
|
||||
// TLS 1.3 Key Share
|
||||
|
Ladataan…
Viittaa uudesa ongelmassa
Block a user