|
- package trs
-
- import (
- "bytes"
- "crypto"
- "crypto/ecdsa"
- "crypto/elliptic"
- "crypto/hmac"
- "crypto/rsa"
- "crypto/subtle"
- "encoding/hex"
- "errors"
- "fmt"
- "hash"
- "io"
- "log"
- "os"
- "runtime"
- "runtime/debug"
- "strings"
- "sync/atomic"
- "time"
-
- sidh "github.com/cloudflare/sidh/sidh"
- sike "github.com/cloudflare/sidh/sike"
- "golang.org/x/crypto/curve25519"
- )
-
- // numSessionTickets is the number of different session tickets the
- // server sends to a TLS 1.3 client, who will use each only once.
- const numSessionTickets = 2
-
- type secretLabel int
-
- const (
- // Both public key and shared secret size
- x25519Sz = 32
-
- 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 (
- secretResumptionPskBinder secretLabel = iota
- secretEarlyClient
- secretHandshakeClient
- secretHandshakeServer
- secretApplicationClient
- secretApplicationServer
- secretResumption
- )
-
- type keySchedule13 struct {
- suite *cipherSuite
- transcriptHash hash.Hash // uses the cipher suite hash algo
- secret []byte // Current secret as used for Derive-Secret
- handshakeCtx []byte // cached handshake context, invalidated on updates.
- clientRandom []byte // Used for keylogging, nil if keylogging is disabled.
- config *Config // Used for KeyLogWriter callback, nil if keylogging is disabled.
- }
-
- // Interface implemented by key exchange strategies
- type kex 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)
- // keyAgreementClient declares an API for implementing shared secret agreement on
- // the client side. `c` is a context of current TLS handshake, `ks` is a public key
- // received from the server, ``privateKey`` client private key.
- // Function returns shared secret in case of success or non nil error otherwise.
- keyAgreementClient(c *Conn, ks keyShare, privateKey []byte) ([]byte, error)
- // keyAgreementServer declares an API for implementing shared secret agreement on
- // the server side. `c` context of current TLS handshake, `ks` is a public key
- // received from the client side of the connection, ``privateKey`` is a private key
- // of a server.
- // Function returns secret shared between parties and public value to exchange
- // between parties. In case of failure `error` must be set.
- keyAgreementServer(c *Conn, ks keyShare) ([]byte, keyShare, error)
- }
-
- // defaultServerKEX is an abstract class defining default, common behaviour on
- // a server side.
- type defaultServerKEX struct{}
-
- // defaultServerKEX is an abstract class defining default implementation of
- // server side key agreement. It generates ephemeral key and uses it together
- // with client public part in order to calculate shared secret.
- func (defaultServerKEX) keyAgreementServer(c *Conn, clientKS keyShare) ([]byte, keyShare, error) {
- privateKey, publicKey, err := c.generateKeyShare(clientKS.group)
- if err != nil {
- c.sendAlert(alertInternalError)
- return nil, keyShare{}, err
- }
-
- // Use same key agreement implementation as on the client side
- ss, err := c.keyAgreementClient(clientKS, privateKey)
- if err != nil {
- c.sendAlert(alertIllegalParameter)
- return nil, keyShare{}, err
- }
- return ss, publicKey, nil
- }
-
- // Key Exchange strategies per curve type
- 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{},
- CurveP384: &kexNIST{},
- CurveP521: &kexNIST{},
- X25519: &kexX25519{},
- HybridSIDHp503Curve25519: &kexHybridSIDHp503X25519{},
- HybridSIKEp503Curve25519: &kexHybridSIKEp503X25519{},
- }
-
- func newKeySchedule13(suite *cipherSuite, config *Config, clientRandom []byte) *keySchedule13 {
- if config.KeyLogWriter == nil {
- clientRandom = nil
- config = nil
- }
- return &keySchedule13{
- suite: suite,
- transcriptHash: hashForSuite(suite).New(),
- clientRandom: clientRandom,
- config: config,
- }
- }
-
- // setSecret sets the early/handshake/master secret based on the given secret
- // (IKM). The salt is based on previous secrets (nil for the early secret).
- func (ks *keySchedule13) setSecret(secret []byte) {
- hash := hashForSuite(ks.suite)
- salt := ks.secret
- if salt != nil {
- h0 := hash.New().Sum(nil)
- salt = hkdfExpandLabel(hash, salt, h0, "derived", hash.Size())
- }
- 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
- ks.transcriptHash.Write(data)
- }
-
- func (ks *keySchedule13) getLabel(secretLabel secretLabel) (label, keylogType string) {
- switch secretLabel {
- case secretResumptionPskBinder:
- label = "res binder"
- case secretEarlyClient:
- label = "c e traffic"
- keylogType = "CLIENT_EARLY_TRAFFIC_SECRET"
- case secretHandshakeClient:
- label = "c hs traffic"
- keylogType = "CLIENT_HANDSHAKE_TRAFFIC_SECRET"
- case secretHandshakeServer:
- label = "s hs traffic"
- keylogType = "SERVER_HANDSHAKE_TRAFFIC_SECRET"
- case secretApplicationClient:
- label = "c ap traffic"
- keylogType = "CLIENT_TRAFFIC_SECRET_0"
- case secretApplicationServer:
- label = "s ap traffic"
- keylogType = "SERVER_TRAFFIC_SECRET_0"
- case secretResumption:
- label = "res master"
- }
- return
- }
-
- // deriveSecret returns the secret derived from the handshake context and label.
- func (ks *keySchedule13) deriveSecret(secretLabel secretLabel) []byte {
- label, keylogType := ks.getLabel(secretLabel)
- if ks.handshakeCtx == nil {
- ks.handshakeCtx = ks.transcriptHash.Sum(nil)
- }
- hash := hashForSuite(ks.suite)
- secret := hkdfExpandLabel(hash, ks.secret, ks.handshakeCtx, label, hash.Size())
- if keylogType != "" && ks.config != nil {
- ks.config.writeKeyLog(keylogType, ks.clientRandom, secret)
- }
- return secret
- }
-
- func (ks *keySchedule13) prepareCipher(secretLabel secretLabel) (interface{}, []byte) {
- trafficSecret := ks.deriveSecret(secretLabel)
- hash := hashForSuite(ks.suite)
- key := hkdfExpandLabel(hash, trafficSecret, nil, "key", ks.suite.keyLen)
- iv := hkdfExpandLabel(hash, trafficSecret, nil, "iv", ks.suite.ivLen)
- return ks.suite.aead(key, iv), trafficSecret
- }
-
- func (hs *serverHandshakeState) doTLS13Handshake() error {
- config := hs.c.config
- c := hs.c
-
- hs.c.cipherSuite, hs.hello.cipherSuite = hs.suite.id, hs.suite.id
- // When picking the group for the handshake, priority is given to groups
- // that the client provided a keyShare for, so to avoid a round-trip.
- // After that the order of CurvePreferences is respected.
- var ks keyShare
- CurvePreferenceLoop:
- for _, curveID := range config.curvePreferences() {
- for _, keyShare := range hs.clientHello.keyShares {
- if curveID == keyShare.group {
- ks = keyShare
- break CurvePreferenceLoop
- }
- }
- }
-
- hash := hashForSuite(hs.suite)
- hashSize := hash.Size()
- hs.keySchedule = newKeySchedule13(hs.suite, config, hs.clientHello.random)
-
- // Check for PSK and update key schedule with new early secret key
- isResumed, pskAlert := hs.checkPSK()
- switch {
- case pskAlert != alertSuccess:
- c.sendAlert(pskAlert)
- return errors.New("tls: invalid client PSK")
- case !isResumed:
- // apply an empty PSK if not resumed.
- hs.keySchedule.setSecret(nil)
- case isResumed:
- c.didResume = true
- }
-
- hs.keySchedule.write(hs.clientHello.marshal())
- earlyClientCipher, _ := hs.keySchedule.prepareCipher(secretEarlyClient)
-
- if ks.group == 0 {
- c.sendAlert(alertInternalError)
- return errors.New("tls: HelloRetryRequest not implemented") // TODO(filippo)
- }
- sharedSecret, serverKS, err := c.keyAgreementServer(ks)
- if err != nil {
- return err
- }
- hs.hello.keyShare = serverKS
-
- hs.keySchedule.write(hs.hello.marshal())
- if _, err := c.writeRecord(recordTypeHandshake, hs.hello.marshal()); err != nil {
- return err
- }
-
- // middlebox compatibility mode: send CCS after first handshake message
- if _, err := c.writeRecord(recordTypeChangeCipherSpec, []byte{1}); err != nil {
- return err
- }
-
- hs.keySchedule.setSecret(sharedSecret)
- clientCipher, cTrafficSecret := hs.keySchedule.prepareCipher(secretHandshakeClient)
- hs.hsClientCipher = clientCipher
- serverCipher, sTrafficSecret := hs.keySchedule.prepareCipher(secretHandshakeServer)
- c.out.setCipher(c.vers, serverCipher)
-
- serverFinishedKey := hkdfExpandLabel(hash, sTrafficSecret, nil, "finished", hashSize)
- hs.clientFinishedKey = hkdfExpandLabel(hash, cTrafficSecret, nil, "finished", hashSize)
-
- // EncryptedExtensions
- hs.keySchedule.write(hs.hello13Enc.marshal())
- if _, err := c.writeRecord(recordTypeHandshake, hs.hello13Enc.marshal()); err != nil {
- return err
- }
-
- // TODO: we should have 2 separated methods - one for full-handshake and the other for PSK-handshake
- if !c.didResume {
- // Server MUST NOT send CertificateRequest if authenticating with PSK
- if c.config.ClientAuth >= RequestClientCert {
-
- certReq := new(certificateRequestMsg13)
- // extension 'signature_algorithms' MUST be specified
- certReq.supportedSignatureAlgorithms = supportedSignatureAlgorithms13
- certReq.supportedSignatureAlgorithmsCert = supportedSigAlgorithmsCert(supportedSignatureAlgorithms13)
- hs.keySchedule.write(certReq.marshal())
- if _, err := hs.c.writeRecord(recordTypeHandshake, certReq.marshal()); err != nil {
- return err
- }
- }
-
- if err := hs.sendCertificate13(); err != nil {
- return err
- }
- }
-
- verifyData := hmacOfSum(hash, hs.keySchedule.transcriptHash, serverFinishedKey)
- serverFinished := &finishedMsg{
- verifyData: verifyData,
- }
- hs.keySchedule.write(serverFinished.marshal())
- if _, err := c.writeRecord(recordTypeHandshake, serverFinished.marshal()); err != nil {
- return err
- }
-
- hs.keySchedule.setSecret(nil) // derive master secret
- hs.appClientCipher, _ = hs.keySchedule.prepareCipher(secretApplicationClient)
- serverCipher, _ = hs.keySchedule.prepareCipher(secretApplicationServer)
- c.out.setCipher(c.vers, serverCipher)
-
- if c.hand.Len() > 0 {
- return c.sendAlert(alertUnexpectedMessage)
- }
- if hs.hello13Enc.earlyData {
- c.in.setCipher(c.vers, earlyClientCipher)
- c.phase = readingEarlyData
- } else if hs.clientHello.earlyData {
- c.in.setCipher(c.vers, hs.hsClientCipher)
- c.phase = discardingEarlyData
- } else {
- c.in.setCipher(c.vers, hs.hsClientCipher)
- c.phase = waitingClientFinished
- }
-
- hs.c.Group = hs.hello.keyShare.group
- return nil
- }
-
- // readClientFinished13 is called during the server handshake (when no early
- // data it available) or after reading all early data. It discards early data if
- // the server did not accept it and then verifies the Finished message. Once
- // done it sends the session tickets. Under c.in lock.
- func (hs *serverHandshakeState) readClientFinished13(hasConfirmLock bool) error {
- c := hs.c
-
- // If the client advertised and sends early data while the server does
- // not accept it, it must be fully skipped until the Finished message.
- for c.phase == discardingEarlyData {
- if err := c.readRecord(recordTypeApplicationData); err != nil {
- return err
- }
- // Assume receipt of Finished message (will be checked below).
- if c.hand.Len() > 0 {
- c.phase = waitingClientFinished
- break
- }
- }
-
- // If the client sends early data followed by a Finished message (but
- // no end_of_early_data), the server MUST terminate the connection.
- if c.phase != waitingClientFinished {
- c.sendAlert(alertUnexpectedMessage)
- return errors.New("tls: did not expect Client Finished yet")
- }
-
- c.phase = readingClientFinished
- msg, err := c.readHandshake()
- if err != nil {
- return err
- }
-
- // client authentication
- // (4.4.2) Client MUST send certificate msg if requested by server
- if c.config.ClientAuth >= RequestClientCert && !c.didResume {
- certMsg, ok := msg.(*certificateMsg13)
- if !ok {
- c.sendAlert(alertCertificateRequired)
- return unexpectedMessageError(certMsg, msg)
- }
-
- hs.keySchedule.write(certMsg.marshal())
- certs := getCertsFromEntries(certMsg.certificates)
- pubKey, err := hs.processCertsFromClient(certs)
- if err != nil {
- return err
- }
-
- if len(certs) > 0 {
- // 4.4.3: CertificateVerify MUST appear immediately after Certificate msg
- msg, err = c.readHandshake()
- if err != nil {
- return err
- }
-
- certVerify, ok := msg.(*certificateVerifyMsg)
- if !ok {
- c.sendAlert(alertUnexpectedMessage)
- return unexpectedMessageError(certVerify, msg)
- }
-
- err, alertCode := verifyPeerHandshakeSignature(
- certVerify,
- pubKey,
- supportedSignatureAlgorithms13,
- hs.keySchedule.transcriptHash.Sum(nil),
- "TLS 1.3, client CertificateVerify")
- if err != nil {
- c.sendAlert(alertCode)
- return err
- }
- hs.keySchedule.write(certVerify.marshal())
- }
-
- // Read next chunk
- msg, err = c.readHandshake()
- if err != nil {
- return err
- }
- }
-
- clientFinished, ok := msg.(*finishedMsg)
- if !ok {
- c.sendAlert(alertUnexpectedMessage)
- return unexpectedMessageError(clientFinished, msg)
- }
-
- hash := hashForSuite(hs.suite)
- expectedVerifyData := hmacOfSum(hash, hs.keySchedule.transcriptHash, hs.clientFinishedKey)
- if len(expectedVerifyData) != len(clientFinished.verifyData) ||
- subtle.ConstantTimeCompare(expectedVerifyData, clientFinished.verifyData) != 1 {
- c.sendAlert(alertDecryptError)
- return errors.New("tls: client's Finished message is incorrect")
- }
- hs.keySchedule.write(clientFinished.marshal())
-
- c.hs = nil // Discard the server handshake state
- if c.hand.Len() > 0 {
- return c.sendAlert(alertUnexpectedMessage)
- }
- c.in.setCipher(c.vers, hs.appClientCipher)
- c.in.traceErr, c.out.traceErr = nil, nil
- c.phase = handshakeConfirmed
- atomic.StoreInt32(&c.handshakeConfirmed, 1)
-
- // Any read operation after handshakeRunning and before handshakeConfirmed
- // will be holding this lock, which we release as soon as the confirmation
- // happens, even if the Read call might do more work.
- // If a Handshake is pending, c.confirmMutex will never be locked as
- // ConfirmHandshake will wait for the handshake to complete. If a
- // handshake was complete, and this was a confirmation, unlock
- // c.confirmMutex now to allow readers to proceed.
- if hasConfirmLock {
- c.confirmMutex.Unlock()
- }
- return hs.sendSessionTicket13() // TODO: do in a goroutine
- }
-
- func (hs *serverHandshakeState) sendCertificate13() error {
- c := hs.c
-
- certEntries := []certificateEntry{}
- for _, cert := range hs.cert.Certificate {
- certEntries = append(certEntries, certificateEntry{data: cert})
- }
- if len(certEntries) > 0 && hs.clientHello.ocspStapling {
- certEntries[0].ocspStaple = hs.cert.OCSPStaple
- }
- if len(certEntries) > 0 && hs.clientHello.scts {
- certEntries[0].sctList = hs.cert.SignedCertificateTimestamps
- }
-
- // If hs.delegatedCredential is set (see hs.readClientHello()) then the
- // server is using the delegated credential extension. The DC is added as an
- // extension to the end-entity certificate, i.e., the last CertificateEntry
- // of Certificate.certficate_list. (For details, see
- // https://tools.ietf.org/html/draft-ietf-tls-subcerts-02.)
- if len(certEntries) > 0 && hs.clientHello.delegatedCredential && hs.delegatedCredential != nil {
- certEntries[0].delegatedCredential = hs.delegatedCredential
- }
-
- certMsg := &certificateMsg13{certificates: certEntries}
-
- hs.keySchedule.write(certMsg.marshal())
- if _, err := c.writeRecord(recordTypeHandshake, certMsg.marshal()); err != nil {
- return err
- }
-
- sigScheme, err := hs.selectTLS13SignatureScheme()
- if err != nil {
- c.sendAlert(alertInternalError)
- return err
- }
-
- sigHash := hashForSignatureScheme(sigScheme)
- opts := crypto.SignerOpts(sigHash)
- if signatureSchemeIsPSS(sigScheme) {
- opts = &rsa.PSSOptions{SaltLength: rsa.PSSSaltLengthEqualsHash, Hash: sigHash}
- }
-
- toSign := prepareDigitallySigned(sigHash, "TLS 1.3, server CertificateVerify", hs.keySchedule.transcriptHash.Sum(nil))
- signature, err := hs.privateKey.(crypto.Signer).Sign(c.config.rand(), toSign[:], opts)
- if err != nil {
- c.sendAlert(alertInternalError)
- return err
- }
-
- verifyMsg := &certificateVerifyMsg{
- hasSignatureAndHash: true,
- signatureAlgorithm: sigScheme,
- signature: signature,
- }
- hs.keySchedule.write(verifyMsg.marshal())
- if _, err := c.writeRecord(recordTypeHandshake, verifyMsg.marshal()); err != nil {
- return err
- }
- return nil
- }
-
- func (c *Conn) handleEndOfEarlyData() error {
- if c.phase != readingEarlyData || c.vers < VersionTLS13 {
- return c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage))
- }
- msg, err := c.readHandshake()
- if err != nil {
- return err
- }
- endOfEarlyData, ok := msg.(*endOfEarlyDataMsg)
- // No handshake messages are allowed after EOD.
- if !ok || c.hand.Len() > 0 {
- return c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage))
- }
- c.hs.keySchedule.write(endOfEarlyData.marshal())
- c.phase = waitingClientFinished
- c.in.setCipher(c.vers, c.hs.hsClientCipher)
- return nil
- }
-
- // selectTLS13SignatureScheme chooses the SignatureScheme for the CertificateVerify
- // based on the certificate type and client supported schemes. If no overlap is found,
- // a fallback is selected.
- //
- // See https://tools.ietf.org/html/draft-ietf-tls-tls13-18#section-4.4.1.2
- func (hs *serverHandshakeState) selectTLS13SignatureScheme() (sigScheme SignatureScheme, err error) {
- var supportedSchemes []SignatureScheme
- signer, ok := hs.privateKey.(crypto.Signer)
- if !ok {
- return 0, errors.New("tls: private key does not implement crypto.Signer")
- }
- pk := signer.Public()
- if _, ok := pk.(*rsa.PublicKey); ok {
- sigScheme = PSSWithSHA256
- supportedSchemes = []SignatureScheme{PSSWithSHA256, PSSWithSHA384, PSSWithSHA512}
- } else if pk, ok := pk.(*ecdsa.PublicKey); ok {
- switch pk.Curve {
- case elliptic.P256():
- sigScheme = ECDSAWithP256AndSHA256
- supportedSchemes = []SignatureScheme{ECDSAWithP256AndSHA256}
- case elliptic.P384():
- sigScheme = ECDSAWithP384AndSHA384
- supportedSchemes = []SignatureScheme{ECDSAWithP384AndSHA384}
- case elliptic.P521():
- sigScheme = ECDSAWithP521AndSHA512
- supportedSchemes = []SignatureScheme{ECDSAWithP521AndSHA512}
- default:
- return 0, errors.New("tls: unknown ECDSA certificate curve")
- }
- } else {
- return 0, errors.New("tls: unknown certificate key type")
- }
-
- for _, ss := range supportedSchemes {
- for _, cs := range hs.clientHello.supportedSignatureAlgorithms {
- if ss == cs {
- return ss, nil
- }
- }
- }
-
- return sigScheme, nil
- }
-
- func signatureSchemeIsPSS(s SignatureScheme) bool {
- return s == PSSWithSHA256 || s == PSSWithSHA384 || s == PSSWithSHA512
- }
-
- // hashForSignatureScheme returns the Hash used by a SignatureScheme which is
- // supported by selectTLS13SignatureScheme.
- func hashForSignatureScheme(ss SignatureScheme) crypto.Hash {
- switch ss {
- case PSSWithSHA256, ECDSAWithP256AndSHA256:
- return crypto.SHA256
- case PSSWithSHA384, ECDSAWithP384AndSHA384:
- return crypto.SHA384
- case PSSWithSHA512, ECDSAWithP521AndSHA512:
- return crypto.SHA512
- default:
- panic("unsupported SignatureScheme passed to hashForSignatureScheme")
- }
- }
-
- func hashForSuite(suite *cipherSuite) crypto.Hash {
- if suite.flags&suiteSHA384 != 0 {
- return crypto.SHA384
- }
- return crypto.SHA256
- }
-
- func prepareDigitallySigned(hash crypto.Hash, context string, data []byte) []byte {
- message := bytes.Repeat([]byte{32}, 64)
- message = append(message, context...)
- message = append(message, 0)
- message = append(message, data...)
- h := hash.New()
- h.Write(message)
- return h.Sum(nil)
- }
-
- // generateKeyShare generates keypair. On success it returns private key and keyShare
- // structure with keyShare.group set to supported group ID (as per 4.2.7 in RFC 8446)
- // and keyShare.data set to public key, third argument is nil. On failure, third returned
- // value (an error) contains error message and first two values are undefined.
- func (c *Conn) generateKeyShare(curveID CurveID) ([]byte, keyShare, error) {
- if val, ok := kexStrat[curveID]; ok {
- return val.generate(c, curveID)
- }
- return nil, keyShare{}, errors.New("tls: preferredCurves includes unsupported curve")
- }
-
- // 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) keyAgreementClient(ks keyShare, secretKey []byte) ([]byte, error) {
- if val, ok := kexStrat[ks.group]; ok {
- return val.keyAgreementClient(c, ks, secretKey)
- }
- return nil, errors.New("tls: unsupported group")
- }
-
- // keyAgreementServer generates ephemeral keypair on the on the server side
- // and then uses 'keyShare' (client public key) to derive shared secret
- func (c *Conn) keyAgreementServer(clientKS keyShare) ([]byte, keyShare, error) {
- if val, ok := kexStrat[clientKS.group]; ok {
- return val.keyAgreementServer(c, clientKS)
- }
- return nil, keyShare{}, errors.New("unsupported group")
- }
-
- func hkdfExpandLabel(hash crypto.Hash, secret, hashValue []byte, label string, L int) []byte {
- prefix := "tls13 "
- hkdfLabel := make([]byte, 4+len(prefix)+len(label)+len(hashValue))
- hkdfLabel[0] = byte(L >> 8)
- hkdfLabel[1] = byte(L)
- hkdfLabel[2] = byte(len(prefix) + len(label))
- copy(hkdfLabel[3:], prefix)
- z := hkdfLabel[3+len(prefix):]
- copy(z, label)
- z = z[len(label):]
- z[0] = byte(len(hashValue))
- copy(z[1:], hashValue)
-
- return hkdfExpand(hash, secret, hkdfLabel, L)
- }
-
- func hmacOfSum(f crypto.Hash, hash hash.Hash, key []byte) []byte {
- h := hmac.New(f.New, key)
- h.Write(hash.Sum(nil))
- return h.Sum(nil)
- }
-
- // Maximum allowed mismatch between the stated age of a ticket
- // and the server-observed one. See
- // https://tools.ietf.org/html/draft-ietf-tls-tls13-18#section-4.2.8.2.
- const ticketAgeSkewAllowance = 10 * time.Second
-
- // checkPSK tries to resume using a PSK, returning true (and updating the
- // early secret in the key schedule) if the PSK was used and false otherwise.
- func (hs *serverHandshakeState) checkPSK() (isResumed bool, alert alert) {
- if hs.c.config.SessionTicketsDisabled {
- return false, alertSuccess
- }
-
- foundDHE := false
- for _, mode := range hs.clientHello.pskKeyExchangeModes {
- if mode == pskDHEKeyExchange {
- foundDHE = true
- break
- }
- }
- if !foundDHE {
- return false, alertSuccess
- }
-
- hash := hashForSuite(hs.suite)
- hashSize := hash.Size()
- for i := range hs.clientHello.psks {
- sessionTicket := append([]uint8{}, hs.clientHello.psks[i].identity...)
- if hs.c.config.SessionTicketSealer != nil {
- var ok bool
- sessionTicket, ok = hs.c.config.SessionTicketSealer.Unseal(hs.clientHelloInfo(), sessionTicket)
- if !ok {
- continue
- }
- } else {
- sessionTicket, _ = hs.c.decryptTicket(sessionTicket)
- if sessionTicket == nil {
- continue
- }
- }
- s := &sessionState13{}
- if s.unmarshal(sessionTicket) != alertSuccess {
- continue
- }
- if s.vers != hs.c.vers {
- continue
- }
- clientAge := time.Duration(hs.clientHello.psks[i].obfTicketAge-s.ageAdd) * time.Millisecond
- serverAge := time.Since(time.Unix(int64(s.createdAt), 0))
- if clientAge-serverAge > ticketAgeSkewAllowance || clientAge-serverAge < -ticketAgeSkewAllowance {
- // XXX: NSS is off spec and sends obfuscated_ticket_age as seconds
- clientAge = time.Duration(hs.clientHello.psks[i].obfTicketAge-s.ageAdd) * time.Second
- if clientAge-serverAge > ticketAgeSkewAllowance || clientAge-serverAge < -ticketAgeSkewAllowance {
- continue
- }
- }
-
- // This enforces the stricter 0-RTT requirements on all ticket uses.
- // The benefit of using PSK+ECDHE without 0-RTT are small enough that
- // we can give them up in the edge case of changed suite or ALPN or SNI.
- if s.suite != hs.suite.id {
- continue
- }
- if s.alpnProtocol != hs.c.clientProtocol {
- continue
- }
- if s.SNI != hs.c.serverName {
- continue
- }
-
- hs.keySchedule.setSecret(s.pskSecret)
- binderKey := hs.keySchedule.deriveSecret(secretResumptionPskBinder)
- binderFinishedKey := hkdfExpandLabel(hash, binderKey, nil, "finished", hashSize)
- chHash := hash.New()
- chHash.Write(hs.clientHello.rawTruncated)
- expectedBinder := hmacOfSum(hash, chHash, binderFinishedKey)
-
- if subtle.ConstantTimeCompare(expectedBinder, hs.clientHello.psks[i].binder) != 1 {
- return false, alertDecryptError
- }
-
- if i == 0 && hs.clientHello.earlyData {
- // This is a ticket intended to be used for 0-RTT
- if s.maxEarlyDataLen == 0 {
- // But we had not tagged it as such.
- return false, alertIllegalParameter
- }
- if hs.c.config.Accept0RTTData {
- hs.c.binder = expectedBinder
- hs.c.ticketMaxEarlyData = int64(s.maxEarlyDataLen)
- hs.hello13Enc.earlyData = true
- }
- }
- hs.hello.psk = true
- hs.hello.pskIdentity = uint16(i)
- return true, alertSuccess
- }
-
- return false, alertSuccess
- }
-
- func (hs *serverHandshakeState) sendSessionTicket13() error {
- c := hs.c
- if c.config.SessionTicketsDisabled {
- return nil
- }
-
- foundDHE := false
- for _, mode := range hs.clientHello.pskKeyExchangeModes {
- if mode == pskDHEKeyExchange {
- foundDHE = true
- break
- }
- }
- if !foundDHE {
- return nil
- }
-
- resumptionMasterSecret := hs.keySchedule.deriveSecret(secretResumption)
-
- ageAddBuf := make([]byte, 4)
- sessionState := &sessionState13{
- vers: c.vers,
- suite: hs.suite.id,
- createdAt: uint64(time.Now().Unix()),
- alpnProtocol: c.clientProtocol,
- SNI: c.serverName,
- maxEarlyDataLen: c.config.Max0RTTDataSize,
- }
- hash := hashForSuite(hs.suite)
-
- for i := 0; i < numSessionTickets; i++ {
- if _, err := io.ReadFull(c.config.rand(), ageAddBuf); err != nil {
- c.sendAlert(alertInternalError)
- return err
- }
- sessionState.ageAdd = uint32(ageAddBuf[0])<<24 | uint32(ageAddBuf[1])<<16 |
- uint32(ageAddBuf[2])<<8 | uint32(ageAddBuf[3])
- // ticketNonce must be a unique value for this connection.
- // Assume there are no more than 255 tickets, otherwise two
- // tickets might have the same PSK which could be a problem if
- // one of them is compromised.
- ticketNonce := []byte{byte(i)}
- sessionState.pskSecret = hkdfExpandLabel(hash, resumptionMasterSecret, ticketNonce, "resumption", hash.Size())
- ticket := sessionState.marshal()
- var err error
- if c.config.SessionTicketSealer != nil {
- cs := c.ConnectionState()
- ticket, err = c.config.SessionTicketSealer.Seal(&cs, ticket)
- } else {
- ticket, err = c.encryptTicket(ticket)
- }
- if err != nil {
- c.sendAlert(alertInternalError)
- return err
- }
- if ticket == nil {
- continue
- }
- ticketMsg := &newSessionTicketMsg13{
- lifetime: 24 * 3600, // TODO(filippo)
- maxEarlyDataLength: c.config.Max0RTTDataSize,
- withEarlyDataInfo: c.config.Max0RTTDataSize > 0,
- ageAdd: sessionState.ageAdd,
- nonce: ticketNonce,
- ticket: ticket,
- }
- if _, err := c.writeRecord(recordTypeHandshake, ticketMsg.marshal()); err != nil {
- return err
- }
- }
-
- return nil
- }
-
- func (hs *serverHandshakeState) traceErr(err error) {
- if err == nil {
- return
- }
- if os.Getenv("TLSDEBUG") == "error" {
- if hs != nil && hs.clientHello != nil {
- os.Stderr.WriteString(hex.Dump(hs.clientHello.marshal()))
- } else if err == io.EOF {
- return // don't stack trace on EOF before CH
- }
- fmt.Fprintf(os.Stderr, "\n%s\n", debug.Stack())
- }
- if os.Getenv("TLSDEBUG") == "short" {
- var pcs [4]uintptr
- frames := runtime.CallersFrames(pcs[0:runtime.Callers(3, pcs[:])])
- for {
- frame, more := frames.Next()
- if frame.Function != "crypto/tls.(*halfConn).setErrorLocked" &&
- frame.Function != "crypto/tls.(*Conn).sendAlertLocked" &&
- frame.Function != "crypto/tls.(*Conn).sendAlert" {
- file := frame.File[strings.LastIndex(frame.File, "/")+1:]
- log.Printf("%s:%d (%s): %v", file, frame.Line, frame.Function, err)
- return
- }
- if !more {
- break
- }
- }
- }
- }
-
- func getCertsFromEntries(certEntries []certificateEntry) [][]byte {
- certs := make([][]byte, len(certEntries))
- for i, cert := range certEntries {
- certs[i] = cert.data
- }
- return certs
- }
-
- func (hs *clientHandshakeState) processEncryptedExtensions(ee *encryptedExtensionsMsg) error {
- c := hs.c
- if ee.alpnProtocol != "" {
- c.clientProtocol = ee.alpnProtocol
- c.clientProtocolFallback = false
- }
- return nil
- }
-
- func verifyPeerHandshakeSignature(
- certVerify *certificateVerifyMsg,
- pubKey crypto.PublicKey,
- signAlgosKnown []SignatureScheme,
- transHash []byte,
- contextString string) (error, alert) {
-
- _, sigType, hashFunc, err := pickSignatureAlgorithm(
- pubKey,
- []SignatureScheme{certVerify.signatureAlgorithm},
- signAlgosKnown,
- VersionTLS13)
- if err != nil {
- return err, alertHandshakeFailure
- }
-
- digest := prepareDigitallySigned(hashFunc, contextString, transHash)
- err = verifyHandshakeSignature(sigType, pubKey, hashFunc, digest, certVerify.signature)
-
- if err != nil {
- return err, alertDecryptError
- }
-
- return nil, alertSuccess
- }
-
- func (hs *clientHandshakeState) getCertificate13(certReq *certificateRequestMsg13) (*Certificate, error) {
- certReq12 := &certificateRequestMsg{
- hasSignatureAndHash: true,
- supportedSignatureAlgorithms: certReq.supportedSignatureAlgorithms,
- certificateAuthorities: certReq.certificateAuthorities,
- }
-
- var rsaAvail, ecdsaAvail bool
- for _, sigAlg := range certReq.supportedSignatureAlgorithms {
- switch signatureFromSignatureScheme(sigAlg) {
- case signaturePKCS1v15, signatureRSAPSS:
- rsaAvail = true
- case signatureECDSA:
- ecdsaAvail = true
- }
- }
- if rsaAvail {
- certReq12.certificateTypes = append(certReq12.certificateTypes, certTypeRSASign)
- }
- if ecdsaAvail {
- certReq12.certificateTypes = append(certReq12.certificateTypes, certTypeECDSASign)
- }
-
- return hs.getCertificate(certReq12)
- }
-
- func (hs *clientHandshakeState) sendCertificate13(chainToSend *Certificate, certReq *certificateRequestMsg13) error {
- c := hs.c
-
- certEntries := []certificateEntry{}
- for _, cert := range chainToSend.Certificate {
- certEntries = append(certEntries, certificateEntry{data: cert})
- }
- certMsg := &certificateMsg13{certificates: certEntries}
-
- hs.keySchedule.write(certMsg.marshal())
- if _, err := c.writeRecord(recordTypeHandshake, certMsg.marshal()); err != nil {
- return err
- }
-
- if len(certEntries) == 0 {
- // No client cert available, nothing to sign.
- return nil
- }
-
- key, ok := chainToSend.PrivateKey.(crypto.Signer)
- if !ok {
- c.sendAlert(alertInternalError)
- return fmt.Errorf("tls: client certificate private key of type %T does not implement crypto.Signer", chainToSend.PrivateKey)
- }
-
- signatureAlgorithm, sigType, hashFunc, err := pickSignatureAlgorithm(key.Public(), certReq.supportedSignatureAlgorithms, hs.hello.supportedSignatureAlgorithms, c.vers)
- if err != nil {
- hs.c.sendAlert(alertHandshakeFailure)
- return err
- }
-
- digest := prepareDigitallySigned(hashFunc, "TLS 1.3, client CertificateVerify", hs.keySchedule.transcriptHash.Sum(nil))
- signOpts := crypto.SignerOpts(hashFunc)
- if sigType == signatureRSAPSS {
- signOpts = &rsa.PSSOptions{SaltLength: rsa.PSSSaltLengthEqualsHash, Hash: hashFunc}
- }
- signature, err := key.Sign(c.config.rand(), digest, signOpts)
- if err != nil {
- c.sendAlert(alertInternalError)
- return err
- }
-
- verifyMsg := &certificateVerifyMsg{
- hasSignatureAndHash: true,
- signatureAlgorithm: signatureAlgorithm,
- signature: signature,
- }
- hs.keySchedule.write(verifyMsg.marshal())
- if _, err := c.writeRecord(recordTypeHandshake, verifyMsg.marshal()); err != nil {
- return err
- }
-
- return nil
- }
-
- func (hs *clientHandshakeState) doTLS13Handshake() error {
- c := hs.c
- hash := hashForSuite(hs.suite)
- hashSize := hash.Size()
- serverHello := hs.serverHello
- c.scts = serverHello.scts
-
- // middlebox compatibility mode, send CCS before second flight.
- if _, err := c.writeRecord(recordTypeChangeCipherSpec, []byte{1}); err != nil {
- return err
- }
-
- // TODO check if keyshare is unacceptable, raise HRR.
-
- clientKS := hs.hello.keyShares[0]
- if serverHello.keyShare.group != clientKS.group {
- c.sendAlert(alertIllegalParameter)
- return errors.New("bad or missing key share from server")
- }
-
- // 0-RTT is not supported yet, so use an empty PSK.
- hs.keySchedule.setSecret(nil)
- sharedSecret, err := c.keyAgreementClient(serverHello.keyShare, hs.privateKey)
- if err != nil {
- c.sendAlert(alertIllegalParameter)
- return err
- }
-
- // Calculate handshake secrets.
- hs.keySchedule.setSecret(sharedSecret)
- clientCipher, clientHandshakeSecret := hs.keySchedule.prepareCipher(secretHandshakeClient)
- serverCipher, serverHandshakeSecret := hs.keySchedule.prepareCipher(secretHandshakeServer)
- if c.hand.Len() > 0 {
- c.sendAlert(alertUnexpectedMessage)
- return errors.New("tls: unexpected data after Server Hello")
- }
- // Do not change the sender key yet, the server must authenticate first.
- c.in.setCipher(c.vers, serverCipher)
-
- // Calculate MAC key for Finished messages.
- serverFinishedKey := hkdfExpandLabel(hash, serverHandshakeSecret, nil, "finished", hashSize)
- clientFinishedKey := hkdfExpandLabel(hash, clientHandshakeSecret, nil, "finished", hashSize)
-
- msg, err := c.readHandshake()
- if err != nil {
- return err
- }
- encryptedExtensions, ok := msg.(*encryptedExtensionsMsg)
- if !ok {
- c.sendAlert(alertUnexpectedMessage)
- return unexpectedMessageError(encryptedExtensions, msg)
- }
- if err := hs.processEncryptedExtensions(encryptedExtensions); err != nil {
- return err
- }
- hs.keySchedule.write(encryptedExtensions.marshal())
-
- // PSKs are not supported, so receive Certificate message.
- msg, err = c.readHandshake()
- if err != nil {
- return err
- }
-
- var chainToSend *Certificate
- certReq, isCertRequested := msg.(*certificateRequestMsg13)
- if isCertRequested {
- hs.keySchedule.write(certReq.marshal())
-
- if chainToSend, err = hs.getCertificate13(certReq); err != nil {
- c.sendAlert(alertInternalError)
- return err
- }
-
- msg, err = c.readHandshake()
- if err != nil {
- return err
- }
- }
-
- certMsg, ok := msg.(*certificateMsg13)
- if !ok {
- c.sendAlert(alertUnexpectedMessage)
- return unexpectedMessageError(certMsg, msg)
- }
- hs.keySchedule.write(certMsg.marshal())
-
- // Validate certificates.
- certs := getCertsFromEntries(certMsg.certificates)
- if err := hs.processCertsFromServer(certs); err != nil {
- return err
- }
-
- // Receive CertificateVerify message.
- msg, err = c.readHandshake()
- if err != nil {
- return err
- }
- certVerifyMsg, ok := msg.(*certificateVerifyMsg)
- if !ok {
- c.sendAlert(alertUnexpectedMessage)
- return unexpectedMessageError(certVerifyMsg, msg)
- }
-
- // Validate the DC if present. The DC is only processed if the extension was
- // indicated by the ClientHello; otherwise this call will result in an
- // "illegal_parameter" alert.
- if len(certMsg.certificates) > 0 {
- if err := hs.processDelegatedCredentialFromServer(
- certMsg.certificates[0].delegatedCredential,
- certVerifyMsg.signatureAlgorithm); err != nil {
- return err
- }
- }
-
- // Set the public key used to verify the handshake.
- pk := hs.c.peerCertificates[0].PublicKey
-
- // If the delegated credential extension has successfully been negotiated,
- // then the CertificateVerify signature will have been produced with the
- // DelegatedCredential's private key.
- if hs.c.verifiedDc != nil {
- pk = hs.c.verifiedDc.cred.publicKey
- }
-
- // Verify the handshake signature.
- err, alertCode := verifyPeerHandshakeSignature(
- certVerifyMsg,
- pk,
- hs.hello.supportedSignatureAlgorithms,
- hs.keySchedule.transcriptHash.Sum(nil),
- "TLS 1.3, server CertificateVerify")
- if err != nil {
- c.sendAlert(alertCode)
- return err
- }
- hs.keySchedule.write(certVerifyMsg.marshal())
-
- // Receive Finished message.
- msg, err = c.readHandshake()
- if err != nil {
- return err
- }
- serverFinished, ok := msg.(*finishedMsg)
- if !ok {
- c.sendAlert(alertUnexpectedMessage)
- return unexpectedMessageError(serverFinished, msg)
- }
- // Validate server Finished hash.
- expectedVerifyData := hmacOfSum(hash, hs.keySchedule.transcriptHash, serverFinishedKey)
- if subtle.ConstantTimeCompare(expectedVerifyData, serverFinished.verifyData) != 1 {
- c.sendAlert(alertDecryptError)
- return errors.New("tls: server's Finished message is incorrect")
- }
- hs.keySchedule.write(serverFinished.marshal())
-
- // Server has authenticated itself. Calculate application traffic secrets.
- hs.keySchedule.setSecret(nil) // derive master secret
- appServerCipher, _ := hs.keySchedule.prepareCipher(secretApplicationServer)
- appClientCipher, _ := hs.keySchedule.prepareCipher(secretApplicationClient)
- // TODO store initial traffic secret key for KeyUpdate GH #85
-
- // Change outbound handshake cipher for final step
- c.out.setCipher(c.vers, clientCipher)
-
- // Client auth requires sending a (possibly empty) Certificate followed
- // by a CertificateVerify message (if there was an actual certificate).
- if isCertRequested {
- if err := hs.sendCertificate13(chainToSend, certReq); err != nil {
- return err
- }
- }
-
- // Send Finished
- verifyData := hmacOfSum(hash, hs.keySchedule.transcriptHash, clientFinishedKey)
- clientFinished := &finishedMsg{
- verifyData: verifyData,
- }
- if _, err := c.writeRecord(recordTypeHandshake, clientFinished.marshal()); err != nil {
- return err
- }
-
- // Handshake done, set application traffic secret
- c.out.setCipher(c.vers, appClientCipher)
- if c.hand.Len() > 0 {
- c.sendAlert(alertUnexpectedMessage)
- return errors.New("tls: unexpected data after handshake")
- }
- c.in.setCipher(c.vers, appServerCipher)
- hs.c.Group = serverHello.keyShare.group
- return nil
- }
-
- // supportedSigAlgorithmsCert iterates over schemes and filters out those algorithms
- // which are not supported for certificate verification.
- func supportedSigAlgorithmsCert(schemes []SignatureScheme) (ret []SignatureScheme) {
- for _, sig := range schemes {
- // X509 doesn't support PSS signatures
- if !signatureSchemeIsPSS(sig) {
- ret = append(ret, sig)
- }
- }
- return
- }
-
- // Functions below implement kex 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) keyAgreementClient(c *Conn, ks keyShare, secretKey []byte) ([]byte, error) {
- // never fails
- curve, _ := curveForCurveID(ks.group)
- x, y := elliptic.Unmarshal(curve, ks.data)
- if x == nil {
- return nil, errors.New("tls: Point not on a curve")
- }
- x, _ = curve.ScalarMult(x, y, secretKey)
- xBytes := x.Bytes()
- curveSize := (curve.Params().BitSize + 8 - 1) >> 3
- if len(xBytes) == curveSize {
- return xBytes, nil
- }
- buf := make([]byte, curveSize)
- copy(buf[len(buf)-len(xBytes):], xBytes)
- return buf, nil
- }
-
- // KEX: X25519
- func (kexX25519) generate(c *Conn, groupId CurveID) ([]byte, keyShare, error) {
- var scalar, public [x25519Sz]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) keyAgreementClient(c *Conn, ks keyShare, secretKey []byte) ([]byte, error) {
- var theirPublic, sharedKey, scalar [x25519Sz]byte
- if len(ks.data) != x25519Sz {
- return nil, errors.New("tls: wrong shared secret size")
- }
- copy(theirPublic[:], ks.data)
- copy(scalar[:], secretKey)
- curve25519.ScalarMult(&sharedKey, &scalar, &theirPublic)
- return sharedKey[:], nil
- }
-
- // 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: 0 /*UNUSED*/, data: pubKey.Export()}, nil
- }
-
- func (kexSIDHp503) keyAgreementClient(c *Conn, ks keyShare, key []byte) ([]byte, error) {
- var prvVariant, pubVariant = getSidhKeyVariant(c.isClient)
-
- if len(ks.data) != SIDHp503PubKeySz || len(key) != SIDHp503PrvKeySz {
- return nil, errors.New("tls: wrong key size")
- }
-
- prvKey := sidh.NewPrivateKey(sidh.FP_503, prvVariant)
- pubKey := sidh.NewPublicKey(sidh.FP_503, pubVariant)
-
- if err := prvKey.Import(key); err != nil {
- return nil, errors.New("tls: internal error")
- }
- if err := pubKey.Import(ks.data); err != nil {
- return nil, errors.New("tls: internal error")
- }
-
- // Never fails
- sharedKey, _ := sidh.DeriveSecret(prvKey, pubKey)
- return sharedKey, nil
- }
-
- // KEX Hybrid SIDH/503-X25519
- func (kex *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 = kex.classicKEX.generate(c, groupId)
- if err != nil {
- return
- }
- copy(prvHybrid[:], private)
- copy(pubHybrid[:], ks.data)
-
- // Generate PQ ephemeral key for SIDH
- private, ks, err = kex.pqKEX.generate(c, groupId)
- if err != nil {
- return
- }
- copy(prvHybrid[x25519Sz:], private)
- copy(pubHybrid[x25519Sz:], ks.data)
- return prvHybrid[:], keyShare{group: HybridSIDHp503Curve25519, data: pubHybrid[:]}, nil
- }
-
- 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 = theirsKS.data[:x25519Sz]
- ret, err := kex.classicKEX.keyAgreementClient(c, tmpKs, key[:x25519Sz])
- if err != nil {
- return nil, err
- }
- copy(sharedKey[:], ret)
-
- // Key agreement for PQ
- tmpKs.group = 0 /*UNUSED*/
- tmpKs.data = theirsKS.data[x25519Sz:]
- ret, err = kex.pqKEX.keyAgreementClient(c, tmpKs, key[x25519Sz:])
- if err != nil {
- return nil, err
- }
- 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
- }
|