Adds X25519-SIKEp503 key agreement (#159)

* removes useless variable

* [sike] Adds X25519-SIKEp503 with cSHAKE
This commit is contained in:
Henry Case 2019-02-26 10:16:49 +00:00 zatwierdzone przez GitHub
rodzic a5d35123cc
commit 600d4dece5
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 4AEE18F83AFDEB23
6 zmienionych plików z 196 dodań i 29 usunięć

201
13.go
Wyświetl plik

@ -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
}

Wyświetl plik

@ -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

Wyświetl plik

@ -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

Wyświetl plik

@ -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) {

Wyświetl plik

@ -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",

Wyświetl plik

@ -124,6 +124,7 @@ const (
// Experimental KEX
HybridSIDHp503Curve25519 CurveID = 0xFE30
HybridSIKEp503Curve25519 CurveID = 0xFE32
)
// TLS 1.3 Key Share