sidh: adds PQ secure KEX

* SIDH/P503-X25519
* adds interop tests
This commit is contained in:
Henry Case 2018-10-05 22:43:00 +01:00 committed by Kris Kwiatkowski
parent 7c79cbefc5
commit d184bc0099
8 changed files with 270 additions and 68 deletions

249
13.go
View File

@ -21,6 +21,7 @@ import (
"sync/atomic" "sync/atomic"
"time" "time"
sidh "github_com/cloudflare/sidh/sidh"
"golang_org/x/crypto/curve25519" "golang_org/x/crypto/curve25519"
) )
@ -29,11 +30,16 @@ import (
const numSessionTickets = 2 const numSessionTickets = 2
type secretLabel int type secretLabel int
type role uint8
const ( const (
kRole_Server = iota x25519SharedSecretSz = 32
kRole_Client
P503PubKeySz = 378
P503PrvKeySz = 32
P503SharedSecretSz = 126
SidhP503Curve25519PubKeySz = x25519SharedSecretSz + P503PubKeySz
SidhP503Curve25519PrvKeySz = x25519SharedSecretSz + P503PrvKeySz
SidhP503Curve25519SharedKeySz = x25519SharedSecretSz + P503SharedSecretSz
) )
const ( const (
@ -55,6 +61,38 @@ type keySchedule13 struct {
config *Config // Used for KeyLogWriter callback, nil if keylogging is disabled. 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 { func newKeySchedule13(suite *cipherSuite, config *Config, clientRandom []byte) *keySchedule13 {
if config.KeyLogWriter == nil { if config.KeyLogWriter == nil {
clientRandom = nil clientRandom = nil
@ -80,6 +118,15 @@ func (ks *keySchedule13) setSecret(secret []byte) {
ks.secret = hkdfExtract(hash, secret, salt) 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. // write appends the data to the transcript hash context.
func (ks *keySchedule13) write(data []byte) { func (ks *keySchedule13) write(data []byte) {
ks.handshakeCtx = nil ks.handshakeCtx = nil
@ -186,7 +233,7 @@ CurvePreferenceLoop:
earlyClientCipher, _ := hs.keySchedule.prepareCipher(secretEarlyClient) earlyClientCipher, _ := hs.keySchedule.prepareCipher(secretEarlyClient)
ecdheSecret := c.deriveECDHESecret(ks, privateKey) ecdheSecret := c.deriveDHESecret(ks, privateKey)
if ecdheSecret == nil { if ecdheSecret == nil {
c.sendAlert(alertIllegalParameter) c.sendAlert(alertIllegalParameter)
return errors.New("tls: bad ECDHE client share") return errors.New("tls: bad ECDHE client share")
@ -551,63 +598,24 @@ func prepareDigitallySigned(hash crypto.Hash, context string, data []byte) []byt
return h.Sum(nil) 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) { func (c *Conn) generateKeyShare(curveID CurveID) ([]byte, keyShare, error) {
if curveID == X25519 { if val, ok := dhKexStrat[curveID]; ok {
var scalar, public [32]byte return val.generate(c, curveID)
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") return nil, keyShare{}, errors.New("tls: preferredCurves includes unsupported curve")
} }
privateKey, x, y, err := elliptic.GenerateKey(curve, c.config.rand()) // DH key agreement. ks stores public key, secretKey stores private key used for ephemeral
if err != nil { // key agreement. Function returns shared secret in case of success or empty slice otherwise.
return nil, keyShare{}, err func (c *Conn) deriveDHESecret(ks keyShare, secretKey []byte) []byte {
if val, ok := dhKexStrat[ks.group]; ok {
return val.derive(c, ks, secretKey)
} }
ecdhePublic := elliptic.Marshal(curve, x, y)
return privateKey, keyShare{group: curveID, data: ecdhePublic}, nil
}
func (c *Conn) deriveECDHESecret(ks keyShare, secretKey []byte) []byte {
if ks.group == X25519 {
if len(ks.data) != 32 {
return nil 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
}
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
}
func hkdfExpandLabel(hash crypto.Hash, secret, hashValue []byte, label string, L int) []byte { func hkdfExpandLabel(hash crypto.Hash, secret, hashValue []byte, label string, L int) []byte {
prefix := "tls13 " prefix := "tls13 "
hkdfLabel := make([]byte, 4+len(prefix)+len(label)+len(hashValue)) hkdfLabel := make([]byte, 4+len(prefix)+len(label)+len(hashValue))
@ -981,7 +989,7 @@ func (hs *clientHandshakeState) doTLS13Handshake() error {
// 0-RTT is not supported yet, so use an empty PSK. // 0-RTT is not supported yet, so use an empty PSK.
hs.keySchedule.setSecret(nil) hs.keySchedule.setSecret(nil)
ecdheSecret := c.deriveECDHESecret(serverHello.keyShare, hs.privateKey) ecdheSecret := c.deriveDHESecret(serverHello.keyShare, hs.privateKey)
if ecdheSecret == nil { if ecdheSecret == nil {
c.sendAlert(alertIllegalParameter) c.sendAlert(alertIllegalParameter)
return errors.New("tls: bad ECDHE server share") return errors.New("tls: bad ECDHE server share")
@ -1161,3 +1169,138 @@ func supportedSigAlgorithmsCert(schemes []SignatureScheme) (ret []SignatureSchem
} }
return 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[:]
}

View File

@ -15,7 +15,7 @@ OS ?= $(shell $(GO) env GOHOSTOS)
ARCH ?= $(shell $(GO) env GOHOSTARCH) ARCH ?= $(shell $(GO) env GOHOSTARCH)
OS_ARCH := $(OS)_$(ARCH) OS_ARCH := $(OS)_$(ARCH)
VER_OS_ARCH := $(shell $(GO) version | cut -d' ' -f 3)_$(OS)_$(ARCH) VER_OS_ARCH := $(shell $(GO) version | cut -d' ' -f 3)_$(OS)_$(ARCH)
GOROOT_ENV := $(shell $(GO) env GOROOT) GOROOT_ENV ?= $(shell $(GO) env GOROOT)
GOROOT_LOCAL = $(BUILD_DIR)/$(OS_ARCH) GOROOT_LOCAL = $(BUILD_DIR)/$(OS_ARCH)
# Flag indicates wheter invoke "go install -race std". Supported only on amd64 with CGO enabled # Flag indicates wheter invoke "go install -race std". Supported only on amd64 with CGO enabled
INSTALL_RACE:= $(words $(filter $(ARCH)_$(shell go env CGO_ENABLED), amd64_1)) INSTALL_RACE:= $(words $(filter $(ARCH)_$(shell go env CGO_ENABLED), amd64_1))
@ -30,8 +30,10 @@ BOGO_DOCKER_TRIS_LOCATION=/go/src/github.com/cloudflare/tls-tris
# SIDH repository (TODO: change path) # SIDH repository (TODO: change path)
SIDH_REPO ?= https://github.com/cloudflare/sidh.git SIDH_REPO ?= https://github.com/cloudflare/sidh.git
SIDH_REPO_TAG ?= 25c0d9f15d5e5bd5652b9741aeb50d9eb37154dc
# NOBS repo (SIKE depends on SHA3) # NOBS repo (SIKE depends on SHA3)
NOBS_REPO ?= https://github.com/henrydcase/nobscrypto.git NOBS_REPO ?= https://github.com/henrydcase/nobscrypto.git
NOBS_REPO_TAG ?= 597f68906e981e61160b8c959b326596c0b240be
############### ###############
# #
@ -57,11 +59,13 @@ $(BUILD_DIR)/$(OS_ARCH)/.ok_$(VER_OS_ARCH): clean
# Vendor NOBS library # Vendor NOBS library
$(GIT) clone $(NOBS_REPO) $(TMP_DIR)/nobs $(GIT) clone $(NOBS_REPO) $(TMP_DIR)/nobs
cd $(TMP_DIR)/nobs; $(GIT) checkout $(NOBS_REPO_TAG)
cd $(TMP_DIR)/nobs; make vendor-sidh-for-tls cd $(TMP_DIR)/nobs; make vendor-sidh-for-tls
cp -rf $(TMP_DIR)/nobs/tls_vendor/* $(GOROOT_LOCAL)/src/vendor/ cp -rf $(TMP_DIR)/nobs/tls_vendor/* $(GOROOT_LOCAL)/src/vendor/
# Vendor SIDH library # Vendor SIDH library
$(GIT) clone $(SIDH_REPO) $(TMP_DIR)/sidh $(GIT) clone $(SIDH_REPO) $(TMP_DIR)/sidh
cd $(TMP_DIR)/sidh; $(GIT) checkout $(SIDH_REPO_TAG)
cd $(TMP_DIR)/sidh; make vendor cd $(TMP_DIR)/sidh; make vendor
cp -rf $(TMP_DIR)/sidh/build/vendor/* $(GOROOT_LOCAL)/src/vendor/ cp -rf $(TMP_DIR)/sidh/build/vendor/* $(GOROOT_LOCAL)/src/vendor/

View File

@ -216,12 +216,10 @@ class InteropServer_BoringSSL(InteropServer, ServerNominalMixin, ServerClientAut
Checks wether ALPN is sent back by tris server in EncryptedExtensions in case of TLS 1.3. The Checks wether ALPN is sent back by tris server in EncryptedExtensions in case of TLS 1.3. The
ALPN protocol is set to 'npn_proto', which is hardcoded in TRIS test server. ALPN protocol is set to 'npn_proto', which is hardcoded in TRIS test server.
''' '''
res = self.d.run_client(self.CLIENT_NAME, self.server_ip+":1443 "+'-alpn-protos npn_proto -debug') res = self.d.run_client(self.CLIENT_NAME, self.server_ip+":1443 "+'-alpn-protos npn_proto')
print(res[1])
self.assertEqual(res[0], 0) self.assertEqual(res[0], 0)
self.assertIsNotNone(re.search(RE_PATTERN_ALPN, res[1], re.MULTILINE)) self.assertIsNotNone(re.search(RE_PATTERN_ALPN, res[1], re.MULTILINE))
# PicoTLS doesn't seem to implement draft-23 correctly. It will # PicoTLS doesn't seem to implement draft-23 correctly. It will
# be enabled when draft-28 is implemented. # be enabled when draft-28 is implemented.
# class InteropServer_PicoTLS( # class InteropServer_PicoTLS(
@ -262,5 +260,17 @@ class InteropServer_TRIS(ClientNominalMixin, InteropServer, unittest.TestCase):
res = self.d.run_client(self.CLIENT_NAME, '-rsa=false -ecdsa=false -cliauth '+self.server_ip+":6443") res = self.d.run_client(self.CLIENT_NAME, '-rsa=false -ecdsa=false -cliauth '+self.server_ip+":6443")
self.assertEqual(res[0], 0) self.assertEqual(res[0], 0)
def test_qr(self):
res = self.d.run_client(self.CLIENT_NAME, '-rsa=false -ecdsa=true -qr SIDH-P503-X25519 '+self.server_ip+":7443")
self.assertEqual(res[0], 0)
def test_qrServerDoesntSupportSIDH(self):
'''
Client advertises HybridSIDH and ECDH. Server supports ECDH only. Checks weather
TLS session can still be established.
'''
res = self.d.run_client(self.CLIENT_NAME, '-rsa=false -ecdsa=true '+self.server_ip+":7443")
self.assertEqual(res[0], 0)
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()

View File

@ -8,6 +8,7 @@ EXPOSE 3443
EXPOSE 4443 EXPOSE 4443
EXPOSE 5443 EXPOSE 5443
EXPOSE 6443 EXPOSE 6443
EXPOSE 7443
ADD tris-localserver / ADD tris-localserver /
ADD runner.sh / ADD runner.sh /

View File

@ -6,5 +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: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: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: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
wait wait

View File

@ -55,6 +55,15 @@ func NewServer() *server {
return s return s
} }
func enableQR(s *server, enableDefault bool) {
var sidhCurves = []tls.CurveID{tls.HybridSidhP503Curve25519}
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...)
}
func (s *server) start() { func (s *server) start() {
var err error var err error
if (s.ZeroRTT & ZeroRTT_Offer) == ZeroRTT_Offer { if (s.ZeroRTT & ZeroRTT_Offer) == ZeroRTT_Offer {
@ -144,6 +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_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_confirm := flag.Bool("rtt0ack", false, "0-RTT confirm")
arg_clientauth := flag.Bool("cliauth", false, "Performs client authentication (RequireAndVerifyClientCert used)") 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]")
flag.Parse() flag.Parse()
s.Address = *arg_addr s.Address = *arg_addr
@ -162,6 +172,12 @@ func main() {
s.TLS.ClientAuth = tls.RequireAndVerifyClientCert s.TLS.ClientAuth = tls.RequireAndVerifyClientCert
} }
if *arg_qr == "c" {
enableQR(s, true)
} else if *arg_qr == "q" {
enableQR(s, false)
}
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
tlsConn := r.Context().Value(http.TLSConnContextKey).(*tls.Conn) tlsConn := r.Context().Value(http.TLSConnContextKey).(*tls.Conn)

View File

@ -51,6 +51,17 @@ func (c *Client) setMinMaxTLS(ver uint16) {
c.TLS.MaxVersion = ver c.TLS.MaxVersion = ver
} }
func getQrAlgoId(qr string) tls.CurveID {
switch qr {
case "SIDH-P503-X25519":
return tls.HybridSidhP503Curve25519
case "SIDH-P751-X448":
return tls.HybridSidhP751Curve448
default:
return 0
}
}
func (c *Client) run() { func (c *Client) run() {
fmt.Printf("TLS %s with %s\n", tlsVersionToName[c.TLS.MinVersion], cipherSuiteIdToName[c.TLS.CipherSuites[0]]) fmt.Printf("TLS %s with %s\n", tlsVersionToName[c.TLS.MinVersion], cipherSuiteIdToName[c.TLS.CipherSuites[0]])
@ -78,8 +89,7 @@ func (c *Client) run() {
failed++ failed++
return return
} }
fmt.Printf("Read %d bytes\n", n) fmt.Printf("[TLS: %s] Read %d bytes\n", tlsVersionToName[con.ConnectionState().Version], n)
fmt.Println("OK\n") fmt.Println("OK\n")
} }
@ -93,13 +103,14 @@ func result() {
// Usage client args host:port // Usage client args host:port
func main() { func main() {
var keylog_file string var keylog_file, qrAlgoName string
var enable_rsa, enable_ecdsa, client_auth bool var enable_rsa, enable_ecdsa, client_auth bool
flag.StringVar(&keylog_file, "keylogfile", "", "Secrets will be logged here") flag.StringVar(&keylog_file, "keylogfile", "", "Secrets will be logged here")
flag.BoolVar(&enable_rsa, "rsa", true, "Whether to enable RSA cipher suites") flag.BoolVar(&enable_rsa, "rsa", true, "Whether to enable RSA cipher suites")
flag.BoolVar(&enable_ecdsa, "ecdsa", true, "Whether to enable ECDSA cipher suites") flag.BoolVar(&enable_ecdsa, "ecdsa", true, "Whether to enable ECDSA cipher suites")
flag.BoolVar(&client_auth, "cliauth", false, "Whether to enable client authentication") flag.BoolVar(&client_auth, "cliauth", false, "Whether to enable client authentication")
flag.StringVar(&qrAlgoName, "qr", "", "Specifies qr algorithm from following list:\n[SIDH-P503-X25519, SIDH-P751-X448]")
flag.Parse() flag.Parse()
if flag.NArg() != 1 { if flag.NArg() != 1 {
flag.Usage() flag.Usage()
@ -124,6 +135,21 @@ func main() {
log.Println("Enabled keylog") log.Println("Enabled keylog")
} }
if len(qrAlgoName) > 0 {
id := getQrAlgoId(qrAlgoName)
if id == 0 {
log.Fatalf("Unknown QR algorithm: %s", qrAlgoName)
return
}
client.TLS.CipherSuites = []uint16{tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256}
client.TLS.CurvePreferences = []tls.CurveID{id}
client.setMinMaxTLS(tls.VersionTLS13)
client.run()
result()
return
}
if client_auth { if client_auth {
var err error var err error
client_cert, err := tls.X509KeyPair([]byte(client_crt), []byte(client_key)) client_cert, err := tls.X509KeyPair([]byte(client_crt), []byte(client_key))
@ -145,6 +171,7 @@ func main() {
c.setMinMaxTLS(tls.VersionTLS12) c.setMinMaxTLS(tls.VersionTLS12)
c.run() c.run()
} }
if enable_ecdsa { if enable_ecdsa {
// Sane cipher suite for TLS 1.2 with an ECDSA cert (as used by boringssl) // Sane cipher suite for TLS 1.2 with an ECDSA cert (as used by boringssl)
c := client.clone() c := client.clone()

View File

@ -116,10 +116,6 @@ const (
type CurveID uint16 type CurveID uint16
const ( const (
// Unexported
sidhP503 CurveID = 0
sidhP751 CurveID = 1
// Exported IDs // Exported IDs
CurveP256 CurveID = 23 CurveP256 CurveID = 23
CurveP384 CurveID = 24 CurveP384 CurveID = 24
@ -127,8 +123,12 @@ const (
X25519 CurveID = 29 X25519 CurveID = 29
// Experimental KEX // Experimental KEX
HybridSidhP503Curve25519 CurveID = 0x0105 + sidhP503 // HybridSIDH: X25519 + P503 HybridSidhP503Curve25519 CurveID = 0x0105 + (sidhP503 & 0xFF) // HybridSIDH: X25519 + P503
HybridSidhP751Curve448 CurveID = 0x0105 + sidhP751 // HybridSIDH: X448 + P751 HybridSidhP751Curve448 CurveID = 0x0105 + (sidhP751 & 0xFF) // HybridSIDH: X448 + P751
// Internal usage. Deliberately not exported
sidhP503 CurveID = 0xFE00
sidhP751 CurveID = 0xFE01
) )
// TLS 1.3 Key Share // TLS 1.3 Key Share