Compare commits

...

Author SHA1 Message Date
  Kris Kwiatkowski aca40d198f This is implementation of server which is running on crypto.cloudflare.com 5 years ago
  Kris Kwiatkowski c362368c5b Fixes log message 5 years ago
  Henry Case 600d4dece5
Adds X25519-SIKEp503 key agreement (#159) 5 years ago
  Henry Case a5d35123cc
[sike] Refactor key agreement in TLS 1.3 [PATCH 1/2] (#153) 5 years ago
  Henry Case 7619b84b13
Fixes GH#154. Updates client certificates (#155) 5 years ago
  Kris Kwiatkowski 76231e7564 SIDH: Implement test againt TLSv1.2 5 years ago
  Kris Kwiatkowski 334eee5310 SIDH: Align codepoints with the ones in latest version 5 years ago
  Kris Kwiatkowski 1c0d342ecc boringssl: update to newest version 5 years ago
  Kris Kwiatkowski 36f2800cb3 adds LICENSE 6 years ago
  Kris Kwiatkowski fab2c445b3 makefile: use named tags instead of commit numbers 6 years ago
  Kris Kwiatkowski c752e2e7a4 test: adds tests for rsa-pss signatures 6 years ago
  Filippo Valsorda d20745552c crypto/tls: expand docs on client-side session ticket support 6 years ago
  Kris Kwiatkowski 18024f59de crypto/tls: add examples for [Load]X509KeyPair 6 years ago
  Filippo Valsorda 4c970a6672 crypto/tls: parse certificate first in X509KeyPair to get better errors 6 years ago
  Kevin Burke 9fc345bd63 crypto/tls: handle errors in generate_cert.go 6 years ago
  Kris Kwiatkowski 86b0aecf5e go 1.11: Switch to GO 1.11 6 years ago
  Kris Kwiatkowski 8da51abeca sidh: operability tests with BoringSSL 6 years ago
  Kris Kwiatkowski 96db6f14d8 sidh: sidh support in boringssl 6 years ago
  Henry Case cb67de3e1f
sidh: use version with improved vendoring (#144) 6 years ago
  Kris Kwiatkowski bcc2724bf6 sidh: use implementation with improved performance for ARMv8 6 years ago
  Marten Seemann 6fcf1bc4c0 don't expect CertificateVerify when the client doesn't send any cert 6 years ago
  Kris Kwiatkowski ce53b126bc fix: client shouldn't use P751/x448 ID 6 years ago
  Kris Kwiatkowski 3895b624f3 sidh: comment out P751/x448 6 years ago
  Kris Kwiatkowski d184bc0099 sidh: adds PQ secure KEX 6 years ago
  Kris Kwiatkowski 7c79cbefc5 sidh: API 6 years ago
  Kris Kwiatkowski ed36ba540a sidh: updates for Makefile 6 years ago
  Kris Kwiatkowski 61bc4c1a09 moves methods from Config to Conn receives 6 years ago
  Kris Kwiatkowski 07ad1769c3 fix: in TLSv1.3, the ALPN extension must be sent in EE 6 years ago
  Kris Kwiatkowski da110326f8 Swap TLS 1.3 to RFC 8446 6 years ago
  Watson Ladd 7e1760cc7c Add EMS support to tls-tris 7 years ago
  Kris Kwiatkowski 58c559ba00 fix: mac calculation fails when subsequent packet is shorter 6 years ago
  Kris Kwiatkowski 1678dc5074 cleanup: removes Committer interface 6 years ago
  Marten Seemann e20b4d2a9a don't generate a key share with Curve ID 0 in the ServerHello tests 6 years ago
  Marten Seemann 9919e0e977 fix the key_exchange length in the ClientHello and ServerHello tests 6 years ago
  Marten Seemann e7a33a2dc3 cherry-pick https://go-review.googlesource.com/c/go/+/129755 6 years ago
  Henry Case 81871bbad5
tls tris server: allow custom server keypairs (#128) 6 years ago
  Henry Case 2bcf6466b4
removes old draft version indicators (#127) 6 years ago
  Marten Seemann 40eb693877 remove support for generating draft 18-21 ServerHellos (#124) 6 years ago
  Kris Kwiatkowski 242e89da1b refactoring of the tris test client 6 years ago
  Kris Kwiatkowski 63ec8fff02 refactoring of the tris test server 6 years ago
  Henry Case a21fd9c1bc
refactors record encryption code (#122) 6 years ago
  Henry D. Case e81269b57e Revert "Small refactoring of record encryption code" 6 years ago
  Henry D. Case 1782162852 Small refactoring of record encryption code 6 years ago
  Henry D. Case ad86d61c42 Let's use constant instead of hardcoding values (same is done in bssl) 6 years ago
  Henry D. Case 91a6fcebab Cleanup 6 years ago
  Christopher Patton a2fe2d9a71 DC draft-02, last minute change (#121) 6 years ago
  Henry Case e77e39e7aa
server must NOT send version prior to TLS1.3 in supported_versions (#119) 6 years ago
  Henry Case d3e18f99e2
Minimal number of changes needed to udpate to draft-28 (#115) 6 years ago
  Christopher Patton 0d6e4561a6 Add DC test data for tls13draft28 and tls13rfc (#117) 6 years ago
  Christopher Patton 174a68a0fb Update implementation of draft-ietf-tls-subcerts to draft 02 (#108) 6 years ago
  Henry D. Case 77d1fbf262 Don't use VersionTLS13DraftXX anywhere 6 years ago
  Christopher Patton c5280001a4 Remove delegated credential minting from the API 6 years ago
  Christopher Patton 1ea9624098 Use static test data for testing delegated credentials 6 years ago
  Brendan Mc 22d6deb0e7
Merge pull request #95 from cjpatton/subcerts 6 years ago
  Christopher Patton 84fe9084cd Implement the delegated_credential extension for TLS 6 years ago
  Christopher Patton 963d5877be Refactor the keyAgreement interface 6 years ago
  Henry D. Case 3ff71dcdc5 tests: enable client authentication in bogo 6 years ago
  Henry D. Case 6e4abe2d07 TLSv1.3 draft-23: align tests 6 years ago
  Henry D. Case 03138ec18e TLSv1.3 -draft23: Implementation of signature_algorithms_cert 6 years ago
  Henry D. Case 5bdf1af124 TLS1.3 -draft23: Renumber key_share 6 years ago
  Evan Klitzke 67bc308e04 Update client SCT list during TLS 1.3 handshake, fixes #76 6 years ago
  Henry D. Case b1d6c0aeaa Change function name verifyPeerCertificate->verifyPeerHandshakeSignature 6 years ago
  Henry D. Case 91d6db578b CI: Fail build if code is wrongly formatted 6 years ago
  Henry D. Case b6af4dd343 Code formatting for handshake_server_test.go 6 years ago
  Henry D. Case 3f720fc50c Code formatting for TRIS test client and server 6 years ago
48 changed files with 8065 additions and 854 deletions
Unified View
  1. +4
    -1
      .travis.yml
  2. +497
    -111
      13.go
  3. +63
    -0
      LICENSE
  4. +3
    -2
      README.md
  5. +28
    -4
      _dev/Makefile
  6. +15
    -6
      _dev/bogo/Dockerfile
  7. +17
    -4
      _dev/boring/Dockerfile
  8. +32
    -29
      _dev/boring/client_ca.crt
  9. +29
    -22
      _dev/boring/client_rsa.crt
  10. +28
    -25
      _dev/boring/client_rsa.key
  11. +2
    -2
      _dev/boring/run.sh
  12. +8
    -3
      _dev/boring/server.sh
  13. +5149
    -0
      _dev/boring/sidh_ff433815b51c34496bb6bea13e73e29e5c278238.patch
  14. +83
    -29
      _dev/interop_test_runner
  15. +0
    -60
      _dev/patches/88253a956a753213617d95af3f42a23a78798473.patch
  16. +7
    -0
      _dev/tls_examples/Makefile
  17. +75
    -0
      _dev/tls_examples/ems_client.go
  18. +1
    -0
      _dev/tris-localserver/Dockerfile
  19. +7
    -6
      _dev/tris-localserver/runner.sh
  20. +200
    -151
      _dev/tris-localserver/server.go
  21. +198
    -123
      _dev/tris-testclient/client.go
  22. +7
    -1
      _dev/tstclnt/Dockerfile
  23. +20
    -0
      _dev/utils/fmtcheck.sh
  24. +27
    -0
      _dev/utils/pre-commit
  25. +1
    -0
      alert.go
  26. +4
    -2
      auth.go
  27. +101
    -0
      auth_test.go
  28. +9
    -6
      cipher_suites.go
  29. +97
    -41
      common.go
  30. +40
    -16
      conn.go
  31. +77
    -4
      example_test.go
  32. +14
    -6
      generate_cert.go
  33. +76
    -9
      handshake_client.go
  34. +39
    -0
      handshake_client_test.go
  35. +199
    -121
      handshake_messages.go
  36. +16
    -5
      handshake_messages_test.go
  37. +69
    -32
      handshake_server.go
  38. +0
    -2
      handshake_server_test.go
  39. +13
    -14
      key_agreement.go
  40. +16
    -8
      prf.go
  41. +2
    -1
      prf_test.go
  42. +392
    -0
      subcerts.go
  43. +365
    -0
      subcerts_test.go
  44. +11
    -0
      testdata/example-cert.pem
  45. +5
    -0
      testdata/example-key.pem
  46. +10
    -2
      ticket.go
  47. +4
    -5
      tls.go
  48. +5
    -1
      tls_test.go

+ 4
- 1
.travis.yml View File

@@ -5,7 +5,7 @@ services:
- docker - docker


go: go:
- 1.10.x
- 1.11.x


env: env:
- TEST_SUITE=test-unit - TEST_SUITE=test-unit
@@ -15,6 +15,9 @@ env:
matrix: matrix:
fast_finish: true fast_finish: true


before_install:
- make -f _dev/Makefile fmtcheck

install: install:
- sudo pip install docker - sudo pip install docker




+ 497
- 111
13.go View File

@@ -21,6 +21,8 @@ import (
"sync/atomic" "sync/atomic"
"time" "time"


sidh "github_com/cloudflare/sidh/sidh"
sike "github_com/cloudflare/sidh/sike"
"golang_org/x/crypto/curve25519" "golang_org/x/crypto/curve25519"
) )


@@ -30,6 +32,25 @@ const numSessionTickets = 2


type secretLabel int 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 ( const (
secretResumptionPskBinder secretLabel = iota secretResumptionPskBinder secretLabel = iota
secretEarlyClient secretEarlyClient
@@ -49,6 +70,78 @@ 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 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 { func newKeySchedule13(suite *cipherSuite, config *Config, clientRandom []byte) *keySchedule13 {
if config.KeyLogWriter == nil { if config.KeyLogWriter == nil {
clientRandom = nil clientRandom = nil
@@ -74,6 +167,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
@@ -147,23 +249,6 @@ CurvePreferenceLoop:
} }
} }
} }
if ks.group == 0 {
c.sendAlert(alertInternalError)
return errors.New("tls: HelloRetryRequest not implemented") // TODO(filippo)
}

if committer, ok := c.conn.(Committer); ok {
if err := committer.Commit(); err != nil {
return err
}
}

privateKey, serverKS, err := config.generateKeyShare(ks.group)
if err != nil {
c.sendAlert(alertInternalError)
return err
}
hs.hello.keyShare = serverKS


hash := hashForSuite(hs.suite) hash := hashForSuite(hs.suite)
hashSize := hash.Size() hashSize := hash.Size()
@@ -183,14 +268,17 @@ CurvePreferenceLoop:
} }


hs.keySchedule.write(hs.clientHello.marshal()) hs.keySchedule.write(hs.clientHello.marshal())

earlyClientCipher, _ := hs.keySchedule.prepareCipher(secretEarlyClient) earlyClientCipher, _ := hs.keySchedule.prepareCipher(secretEarlyClient)


ecdheSecret := deriveECDHESecret(ks, privateKey)
if ecdheSecret == nil {
c.sendAlert(alertIllegalParameter)
return errors.New("tls: bad ECDHE client share")
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()) hs.keySchedule.write(hs.hello.marshal())
if _, err := c.writeRecord(recordTypeHandshake, hs.hello.marshal()); err != nil { if _, err := c.writeRecord(recordTypeHandshake, hs.hello.marshal()); err != nil {
@@ -202,7 +290,7 @@ CurvePreferenceLoop:
return err return err
} }


hs.keySchedule.setSecret(ecdheSecret)
hs.keySchedule.setSecret(sharedSecret)
clientCipher, cTrafficSecret := hs.keySchedule.prepareCipher(secretHandshakeClient) clientCipher, cTrafficSecret := hs.keySchedule.prepareCipher(secretHandshakeClient)
hs.hsClientCipher = clientCipher hs.hsClientCipher = clientCipher
serverCipher, sTrafficSecret := hs.keySchedule.prepareCipher(secretHandshakeServer) serverCipher, sTrafficSecret := hs.keySchedule.prepareCipher(secretHandshakeServer)
@@ -225,6 +313,7 @@ CurvePreferenceLoop:
certReq := new(certificateRequestMsg13) certReq := new(certificateRequestMsg13)
// extension 'signature_algorithms' MUST be specified // extension 'signature_algorithms' MUST be specified
certReq.supportedSignatureAlgorithms = supportedSignatureAlgorithms13 certReq.supportedSignatureAlgorithms = supportedSignatureAlgorithms13
certReq.supportedSignatureAlgorithmsCert = supportedSigAlgorithmsCert(supportedSignatureAlgorithms13)
hs.keySchedule.write(certReq.marshal()) hs.keySchedule.write(certReq.marshal())
if _, err := hs.c.writeRecord(recordTypeHandshake, certReq.marshal()); err != nil { if _, err := hs.c.writeRecord(recordTypeHandshake, certReq.marshal()); err != nil {
return err return err
@@ -301,11 +390,11 @@ func (hs *serverHandshakeState) readClientFinished13(hasConfirmLock bool) error
} }


// client authentication // client authentication
if certMsg, ok := msg.(*certificateMsg13); ok {
// (4.4.2) Client MUST send certificate msg if requested by server
if c.config.ClientAuth < RequestClientCert {
c.sendAlert(alertUnexpectedMessage)
// (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) return unexpectedMessageError(certMsg, msg)
} }


@@ -316,39 +405,37 @@ func (hs *serverHandshakeState) readClientFinished13(hasConfirmLock bool) error
return err return err
} }


// 4.4.3: CertificateVerify MUST appear immediately after Certificate msg
msg, err = c.readHandshake()
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)
}
certVerify, ok := msg.(*certificateVerifyMsg)
if !ok {
c.sendAlert(alertUnexpectedMessage)
return unexpectedMessageError(certVerify, msg)
}


err, alertCode := verifyPeerCertificate(
certVerify,
pubKey,
supportedSignatureAlgorithms13,
hs.keySchedule.transcriptHash.Sum(nil),
"TLS 1.3, client CertificateVerify")
if err != nil {
c.sendAlert(alertCode)
return err
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())
} }
hs.keySchedule.write(certVerify.marshal())


// Read next chunk // Read next chunk
msg, err = c.readHandshake() msg, err = c.readHandshake()
if err != nil { if err != nil {
return err return err
} }

} else if (c.config.ClientAuth >= RequestClientCert) && !c.didResume {
c.sendAlert(alertCertificateRequired)
return unexpectedMessageError(certMsg, msg)
} }


clientFinished, ok := msg.(*finishedMsg) clientFinished, ok := msg.(*finishedMsg)
@@ -402,6 +489,16 @@ func (hs *serverHandshakeState) sendCertificate13() error {
if len(certEntries) > 0 && hs.clientHello.scts { if len(certEntries) > 0 && hs.clientHello.scts {
certEntries[0].sctList = hs.cert.SignedCertificateTimestamps 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} certMsg := &certificateMsg13{certificates: certEntries}


hs.keySchedule.write(certMsg.marshal()) hs.keySchedule.write(certMsg.marshal())
@@ -422,7 +519,7 @@ func (hs *serverHandshakeState) sendCertificate13() error {
} }


toSign := prepareDigitallySigned(sigHash, "TLS 1.3, server CertificateVerify", hs.keySchedule.transcriptHash.Sum(nil)) toSign := prepareDigitallySigned(sigHash, "TLS 1.3, server CertificateVerify", hs.keySchedule.transcriptHash.Sum(nil))
signature, err := hs.cert.PrivateKey.(crypto.Signer).Sign(c.config.rand(), toSign[:], opts)
signature, err := hs.privateKey.(crypto.Signer).Sign(c.config.rand(), toSign[:], opts)
if err != nil { if err != nil {
c.sendAlert(alertInternalError) c.sendAlert(alertInternalError)
return err return err
@@ -467,9 +564,9 @@ func (c *Conn) handleEndOfEarlyData() error {
// See https://tools.ietf.org/html/draft-ietf-tls-tls13-18#section-4.4.1.2 // See https://tools.ietf.org/html/draft-ietf-tls-tls13-18#section-4.4.1.2
func (hs *serverHandshakeState) selectTLS13SignatureScheme() (sigScheme SignatureScheme, err error) { func (hs *serverHandshakeState) selectTLS13SignatureScheme() (sigScheme SignatureScheme, err error) {
var supportedSchemes []SignatureScheme var supportedSchemes []SignatureScheme
signer, ok := hs.cert.PrivateKey.(crypto.Signer)
signer, ok := hs.privateKey.(crypto.Signer)
if !ok { if !ok {
return 0, errors.New("tls: certificate private key does not implement crypto.Signer")
return 0, errors.New("tls: private key does not implement crypto.Signer")
} }
pk := signer.Public() pk := signer.Public()
if _, ok := pk.(*rsa.PublicKey); ok { if _, ok := pk.(*rsa.PublicKey); ok {
@@ -540,61 +637,33 @@ func prepareDigitallySigned(hash crypto.Hash, context string, data []byte) []byt
return h.Sum(nil) return h.Sum(nil)
} }


func (c *Config) generateKeyShare(curveID CurveID) ([]byte, keyShare, error) {
if curveID == X25519 {
var scalar, public [32]byte
if _, err := io.ReadFull(c.rand(), scalar[:]); err != nil {
return nil, keyShare{}, err
}

curve25519.ScalarBaseMult(&public, &scalar)
return scalar[:], keyShare{group: curveID, data: public[:]}, 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)
} }

curve, ok := curveForCurveID(curveID)
if !ok {
return nil, keyShare{}, errors.New("tls: preferredCurves includes unsupported curve")
}

privateKey, x, y, err := elliptic.GenerateKey(curve, c.rand())
if err != nil {
return nil, keyShare{}, err
}
ecdhePublic := elliptic.Marshal(curve, x, y)

return privateKey, keyShare{group: curveID, data: ecdhePublic}, nil
return nil, keyShare{}, errors.New("tls: preferredCurves includes unsupported curve")
} }


func deriveECDHESecret(ks keyShare, secretKey []byte) []byte {
if ks.group == X25519 {
if len(ks.data) != 32 {
return nil
}

var theirPublic, sharedKey, scalar [32]byte
copy(theirPublic[:], ks.data)
copy(scalar[:], secretKey)
curve25519.ScalarMult(&sharedKey, &scalar, &theirPublic)
return sharedKey[:]
// 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")
}


curve, ok := curveForCurveID(ks.group)
if !ok {
return nil
// 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)
} }
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
return nil, keyShare{}, errors.New("unsupported group")
} }


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 {
@@ -841,7 +910,7 @@ func (hs *clientHandshakeState) processEncryptedExtensions(ee *encryptedExtensio
return nil return nil
} }


func verifyPeerCertificate(
func verifyPeerHandshakeSignature(
certVerify *certificateVerifyMsg, certVerify *certificateVerifyMsg,
pubKey crypto.PublicKey, pubKey crypto.PublicKey,
signAlgosKnown []SignatureScheme, signAlgosKnown []SignatureScheme,
@@ -953,6 +1022,7 @@ func (hs *clientHandshakeState) doTLS13Handshake() error {
hash := hashForSuite(hs.suite) hash := hashForSuite(hs.suite)
hashSize := hash.Size() hashSize := hash.Size()
serverHello := hs.serverHello serverHello := hs.serverHello
c.scts = serverHello.scts


// middlebox compatibility mode, send CCS before second flight. // middlebox compatibility mode, send CCS before second flight.
if _, err := c.writeRecord(recordTypeChangeCipherSpec, []byte{1}); err != nil { if _, err := c.writeRecord(recordTypeChangeCipherSpec, []byte{1}); err != nil {
@@ -969,14 +1039,14 @@ 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 := deriveECDHESecret(serverHello.keyShare, hs.privateKey)
if ecdheSecret == nil {
sharedSecret, err := c.keyAgreementClient(serverHello.keyShare, hs.privateKey)
if err != nil {
c.sendAlert(alertIllegalParameter) c.sendAlert(alertIllegalParameter)
return errors.New("tls: bad ECDHE server share")
return err
} }


// Calculate handshake secrets. // Calculate handshake secrets.
hs.keySchedule.setSecret(ecdheSecret)
hs.keySchedule.setSecret(sharedSecret)
clientCipher, clientHandshakeSecret := hs.keySchedule.prepareCipher(secretHandshakeClient) clientCipher, clientHandshakeSecret := hs.keySchedule.prepareCipher(secretHandshakeClient)
serverCipher, serverHandshakeSecret := hs.keySchedule.prepareCipher(secretHandshakeServer) serverCipher, serverHandshakeSecret := hs.keySchedule.prepareCipher(secretHandshakeServer)
if c.hand.Len() > 0 { if c.hand.Len() > 0 {
@@ -1032,6 +1102,7 @@ func (hs *clientHandshakeState) doTLS13Handshake() error {
return unexpectedMessageError(certMsg, msg) return unexpectedMessageError(certMsg, msg)
} }
hs.keySchedule.write(certMsg.marshal()) hs.keySchedule.write(certMsg.marshal())

// Validate certificates. // Validate certificates.
certs := getCertsFromEntries(certMsg.certificates) certs := getCertsFromEntries(certMsg.certificates)
if err := hs.processCertsFromServer(certs); err != nil { if err := hs.processCertsFromServer(certs); err != nil {
@@ -1048,9 +1119,32 @@ func (hs *clientHandshakeState) doTLS13Handshake() error {
c.sendAlert(alertUnexpectedMessage) c.sendAlert(alertUnexpectedMessage)
return unexpectedMessageError(certVerifyMsg, msg) return unexpectedMessageError(certVerifyMsg, msg)
} }
err, alertCode := verifyPeerCertificate(

// 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, certVerifyMsg,
hs.c.peerCertificates[0].PublicKey,
pk,
hs.hello.supportedSignatureAlgorithms, hs.hello.supportedSignatureAlgorithms,
hs.keySchedule.transcriptHash.Sum(nil), hs.keySchedule.transcriptHash.Sum(nil),
"TLS 1.3, server CertificateVerify") "TLS 1.3, server CertificateVerify")
@@ -1113,3 +1207,295 @@ func (hs *clientHandshakeState) doTLS13Handshake() error {
c.in.setCipher(c.vers, appServerCipher) c.in.setCipher(c.vers, appServerCipher)
return nil 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
}

+ 63
- 0
LICENSE View File

@@ -0,0 +1,63 @@
Copyright (c) 2018 Cloudflare. All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:

* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Cloudflare nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

========================================================================

The code for TLSv1.2 and older TLS versions was derived from the
Golang standard library <https://golang.org/src/crypto/tls/>, available
under the following BSD license:

========================================================================

Copyright (c) 2009 The Go Authors. All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:

* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

+ 3
- 2
README.md View File

@@ -61,7 +61,8 @@ There are number of things that need to be setup before running tests. Most impo


``` ```
git clone https://github.com/cloudflare/tls-tris.git git clone https://github.com/cloudflare/tls-tris.git
cd tls-tris; make -f _dev/Makefile build-all
cd tls-tris; cp _dev/utils/pre-commit .git/hooks/
make -f _dev/Makefile build-all
``` ```


### Testing ### Testing
@@ -70,7 +71,7 @@ We run 3 kinds of test:.


* Unit testing: <br/>``make -f _dev/Makefile test-unit`` * Unit testing: <br/>``make -f _dev/Makefile test-unit``
* Testing against BoringSSL test suite: <br/>``make -f _dev/Makefile test-bogo`` * Testing against BoringSSL test suite: <br/>``make -f _dev/Makefile test-bogo``
* Compatibility testing (see below):<br/>``make -f _dev/Makefile test-compat``
* Compatibility testing (see below):<br/>``make -f _dev/Makefile test-interop``


To run all the tests in one go use: To run all the tests in one go use:
``` ```


+ 28
- 4
_dev/Makefile View File

@@ -8,24 +8,33 @@ BUILD_DIR ?= $(PRJ_DIR)/_dev/GOROOT
# Compiler # Compiler
GO ?= go GO ?= go
DOCKER ?= docker DOCKER ?= docker
GIT ?= git


# Build environment # Build environment
OS ?= $(shell $(GO) env GOHOSTOS) 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))
TMP_DIR := $(shell mktemp -d)


# Test targets used for compatibility testing # Test targets used for compatibility testing
TARGET_TEST_COMPAT=boring picotls tstclnt TARGET_TEST_COMPAT=boring picotls tstclnt


# Some target-specific constants # Some target-specific constants
BORINGSSL_REVISION=1530ef3e
BORINGSSL_REVISION=ff433815b51c34496bb6bea13e73e29e5c278238
BOGO_DOCKER_TRIS_LOCATION=/go/src/github.com/cloudflare/tls-tris BOGO_DOCKER_TRIS_LOCATION=/go/src/github.com/cloudflare/tls-tris


# SIDH repository
SIDH_REPO ?= https://github.com/cloudflare/sidh.git
SIDH_REPO_TAG ?= Release_1.0
# NOBS repo (SIKE depends on SHA3)
NOBS_REPO ?= https://github.com/henrydcase/nobscrypto.git
NOBS_REPO_TAG ?= Release_0.1

############### ###############
# #
# Build targets # Build targets
@@ -48,16 +57,29 @@ $(BUILD_DIR)/$(OS_ARCH)/.ok_$(VER_OS_ARCH): clean
# Apply additional patches # Apply additional patches
for p in $(wildcard $(DEV_DIR)/patches/*); do patch -d "$(GOROOT_LOCAL)" -p1 < "$$p"; done for p in $(wildcard $(DEV_DIR)/patches/*); do patch -d "$(GOROOT_LOCAL)" -p1 < "$$p"; done


# Vendor NOBS library
$(GIT) clone $(NOBS_REPO) $(TMP_DIR)/nobs
cd $(TMP_DIR)/nobs; $(GIT) checkout tags/$(NOBS_REPO_TAG)
cd $(TMP_DIR)/nobs; make vendor-sidh-for-tls
cp -rf $(TMP_DIR)/nobs/tls_vendor/* $(GOROOT_LOCAL)/src/vendor/

# Vendor SIDH library
$(GIT) clone $(SIDH_REPO) $(TMP_DIR)/sidh
cd $(TMP_DIR)/sidh; $(GIT) checkout tags/$(SIDH_REPO_TAG)
cd $(TMP_DIR)/sidh; make vendor
cp -rf $(TMP_DIR)/sidh/build/vendor/* $(GOROOT_LOCAL)/src/vendor/

# Create go package # Create go package
GOARCH=$(ARCH) GOROOT="$(GOROOT_LOCAL)" $(GO) install -v std GOARCH=$(ARCH) GOROOT="$(GOROOT_LOCAL)" $(GO) install -v std
ifeq ($(INSTALL_RACE),1) ifeq ($(INSTALL_RACE),1)
GOARCH=$(ARCH) GOROOT="$(GOROOT_LOCAL)" $(GO) install -race -v std GOARCH=$(ARCH) GOROOT="$(GOROOT_LOCAL)" $(GO) install -race -v std
endif endif
rm -rf $(TMP_DIR)
@touch "$@" @touch "$@"


build-test-%: $(BUILD_DIR)/$(OS_ARCH)/.ok_$(VER_OS_ARCH) build-test-%: $(BUILD_DIR)/$(OS_ARCH)/.ok_$(VER_OS_ARCH)
$(DOCKER) build $(BUILDARG) -t tls-tris:$* $(DEV_DIR)/$* $(DOCKER) build $(BUILDARG) -t tls-tris:$* $(DEV_DIR)/$*
$(DOCKER) build $(BUILDARG) -t $(*)-localserver $(DEV_DIR)/$*
$(DOCKER) build GOARCH=arm64 $(BUILDARG) -t $(*)-localserver $(DEV_DIR)/$*


build-all: \ build-all: \
build-test-tris-client \ build-test-tris-client \
@@ -112,5 +134,7 @@ clean:
clean-all: clean clean-all: clean
rm -rf $(BUILD_DIR) rm -rf $(BUILD_DIR)


fmtcheck:
$(DEV_DIR)/utils/fmtcheck.sh


.PHONY: $(BUILD_DIR) clean build build-test test test-unit test-bogo test-compat
.PHONY: $(BUILD_DIR) clean build build-test test test-unit test-bogo test-interop

+ 15
- 6
_dev/bogo/Dockerfile View File

@@ -1,4 +1,4 @@
FROM golang:1.10-alpine
FROM golang:1.11-alpine


RUN apk add --update \ RUN apk add --update \
git \ git \
@@ -10,8 +10,8 @@ RUN apk add --update \


ENV CGO_ENABLED=0 ENV CGO_ENABLED=0


RUN git clone https://github.com/FiloSottile/crypto-tls-bogo-shim \
/go/src/github.com/FiloSottile/crypto-tls-bogo-shim
RUN git clone https://github.com/henrydcase/crypto-tls-bogo-shim \
/go/src/github.com/henrydcase/crypto-tls-bogo-shim


# Draft 18 with client-tests branch # Draft 18 with client-tests branch
#ARG REVISION=3f5e87d6a1931b6f6930e4eadb7b2d0b2aa7c588 #ARG REVISION=3f5e87d6a1931b6f6930e4eadb7b2d0b2aa7c588
@@ -20,10 +20,19 @@ RUN git clone https://github.com/FiloSottile/crypto-tls-bogo-shim \
#ARG REVISION=81cc32b846c9fe2ea32613287e57a6a0db7bbb9a #ARG REVISION=81cc32b846c9fe2ea32613287e57a6a0db7bbb9a


# Draft 22 with draft22-client branch (client-tests + draft22) # Draft 22 with draft22-client branch (client-tests + draft22)
ARG REVISION=f9729b5e4eafb1f1d313949388c3c2b167e84734
# ARG REVISION=f9729b5e4eafb1f1d313949388c3c2b167e84734


RUN cd /go/src/github.com/FiloSottile/crypto-tls-bogo-shim && \
# Draft 23
#ARG REVISION=d07b9e80a87c871c2569ce4aabd06695336c5dc5

# Draft 23 (+ client authentication)
# ARG REVISION=cd33ad248ae9490854f0077ca046b47cac3735bf

# Draft 28
ARG REVISION=33204d1eaa497819c6325998d7ba6b66316790f3

RUN cd /go/src/github.com/henrydcase/crypto-tls-bogo-shim && \
git checkout $REVISION git checkout $REVISION


WORKDIR /go/src/github.com/FiloSottile/crypto-tls-bogo-shim
WORKDIR /go/src/github.com/henrydcase/crypto-tls-bogo-shim
CMD ["make", "run"] CMD ["make", "run"]

+ 17
- 4
_dev/boring/Dockerfile View File

@@ -3,6 +3,7 @@ FROM alpine
RUN apk add --update \ RUN apk add --update \
git \ git \
cmake \ cmake \
patch \
perl \ perl \
python \ python \
build-base \ build-base \
@@ -34,18 +35,30 @@ RUN mkdir boringssl/build
# ARG REVISION=89917a5 # ARG REVISION=89917a5


# Draft 18 # Draft 18
#ARG REVISION=9b885c5
# ARG REVISION=9b885c5
# Draft 18, but with "bssl server -loop -www" support and build fix # Draft 18, but with "bssl server -loop -www" support and build fix
ARG REVISION=40b24c8154
# ARG REVISION=40b24c8154


# Draft 21 # Draft 21
#ARG REVISION=cd8470f
# ARG REVISION=cd8470f


# Draft 22 # Draft 22
ARG REVISION=1530ef3e
# ARG REVISION=1530ef3e

# Draft 23
# ARG REVISION=cb15cfda29c0c60d8d74145b17c93b43a7667837

# Draft 28
# ARG REVISION=861f384d7bc59241a9df1634ae938d8e75be2d30

# TLS 1.3
ARG REVISION=ff433815b51c34496bb6bea13e73e29e5c278238

ADD sidh_$REVISION.patch /


RUN cd boringssl && git fetch RUN cd boringssl && git fetch
RUN cd boringssl && git checkout $REVISION RUN cd boringssl && git checkout $REVISION
RUN cd boringssl && patch -p1 < /sidh_$REVISION.patch
RUN cd boringssl/build && cmake -GNinja .. RUN cd boringssl/build && cmake -GNinja ..
RUN cd boringssl && ninja -C build RUN cd boringssl && ninja -C build




+ 32
- 29
_dev/boring/client_ca.crt View File

@@ -1,31 +1,34 @@
-----BEGIN CERTIFICATE----- -----BEGIN CERTIFICATE-----
MIIFYDCCA0igAwIBAgIJAPpBgIvtQb1EMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV
BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX
aWRnaXRzIFB0eSBMdGQwHhcNMTgwMjEzMjAxNjA3WhcNMTkwMjEzMjAxNjA3WjBF
MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50
ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC
CgKCAgEAr4xgdmB4DaEh8zRFmg/1ZxYhQZMUP0iQX/Y8nDWxNlcd42p3TgpY1biz
jrq58ln9Om4U/GAn2RmtBAynSBXIlR5oVa44JeMM8Ka8R/dMKyHpF0Nj2EJB9unb
TC33PfzOlnKQxATwevnnhI6tGluWmwvxXUi7WnX0di+nQg9HrIVom3KrmRr2/41y
g497ccYUuNnKE6sewGdGzw045oWZpMDA2Us+MFo1IywOurjaM9bueRhPTcIiQ8RE
h7qb+FRwfxaj9ynZA2PCM7WMSSWCiZJV0uj/pshYF2lvtJcJef4dhwnsYBpc+mgx
2q9qcUBeo3ZHbi1/PRqjwSmcW3yY5cQRbpYp6xFmgmX3oHQkVXS0UlpNVZ+morcS
HEpaK8b76fCFcL5yFsAJkPPfny1IKU+CfaVq60dM/mxbEW6J4mZT/uAiqrCilMC+
FyiATCZur8Ks7p47eZy700DllLod7gWTiuZTgHeQFVoX+jxbCZKlFn5Xspu8ALoK
Mla/q83mICRVy3+eMUsD7DNvoWYpCAYy/oMk0VWfrQ48JkCGbBW2PW/dU2nmqVhY
/11rurkr+1TUvYodnajANtXvUjW1DPOLb4dES4Qc4b7Fw8eFXrARhl5mXiL5HFKR
/VnRshiJ+QwTVkxl+KkZHEm/WS8QD+Zd8leAxh9MCoaU/XrBUBkCAwEAAaNTMFEw
HQYDVR0OBBYEFKUinuD1xRvcNd2Wti/PnBJp7On1MB8GA1UdIwQYMBaAFKUinuD1
xRvcNd2Wti/PnBJp7On1MA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQAD
ggIBAJdJrNBftqkTs2HyuJ3x5RIsTxYh85hJYwNOdFLyzVG6HER9jRCnvmNTjG0O
I5wz5hQvDpwXs4BCCXHQZrTLAi3BEjq3AjrmR/XeGHulbWh3eh8LVu7MiLRgt+Ys
GnL2IaERrbkje24nCCMNPbI3fGDQEhTIYmmX8RJp+5BOJgCycKk6pFgfrjJv2C+d
78pcjlYII6M4vPnr/a08M49Bq6b5ADvIfe5G2KrUvD/+vwoAwv6d/daymHCQ2rY5
kmdVk9VUp3Q4uKoeej4ENJSAUNTV7oTu346oc7q9sJffB5OltqbrE7ichak7lL+v
EjArZHElAhKNFXRZViCMvGDs+7JztqbsfT8Xb6Z27e+WyudB2bOUGm3hKuTIl06D
bA7yUskwEhmkd1CJqO5RLEJjKitOqe6Ye0/GsmPQNDK8GvyXTyGQK5OqBuzEexF0
mlPoIhpSVH3K9SkRTTHvvcbdYlaQLi6gKq2uhbk4PnS2nfBtXqYIy9mxcgBJzLiB
/ydfLcf3GClwgvO1JHp6qAl4CO7oe8jqHpoGuznwi1aqkTyNkQWh0OXq3MS+dyqB
2yXFCFIeKCx18TE1OtuTD3ppBDjpyd0o/a6kYR3FDmdks/J33bGwLsLH3lbN6VjF
PNfNkaE1tfkpSGYsuT1DPxX8aAT4JLUfZ1Si6iO+E0Sj9LXA
MIIF6zCCA9OgAwIBAgIUC4U4HlbkVMrKKTFK0mNrMFDpRskwDQYJKoZIhvcNAQEL
BQAwfTELMAkGA1UEBhMCRlIxDTALBgNVBAgMBFBBQ0ExFzAVBgNVBAcMDkNhZ25l
cyBzdXIgTWVyMSIwIAYDVQQLDBlDZXJ0IFRlc3RpbmcgT3JnYW5pemF0aW9uMSIw
IAYDVQQDDBlDZXJ0IFRlc3RpbmcgT3JnYW5pemF0aW9uMB4XDTE5MDIyMjAwNDIz
OVoXDTQ2MDcwOTAwNDIzOVowfTELMAkGA1UEBhMCRlIxDTALBgNVBAgMBFBBQ0Ex
FzAVBgNVBAcMDkNhZ25lcyBzdXIgTWVyMSIwIAYDVQQLDBlDZXJ0IFRlc3Rpbmcg
T3JnYW5pemF0aW9uMSIwIAYDVQQDDBlDZXJ0IFRlc3RpbmcgT3JnYW5pemF0aW9u
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA0z+DMLb7YIMFFNZpn+ve
NdT7GL9DPyV9ZWSHpUuDyme6Og6Mp5IpCLlKjNXtizX5aQ9xQ746slt70fivSV/r
tiEtayZkcwS7zHtc5f+U/S0hR1q5Zh3DaLQH9diSeuNFQN5pg7zQT5csJFlxf6EB
j/ioSBC+J1E8A2FAh0qDq+TvPPyZEEjcJy0oBuNHUnkC3rwjt24DAUI26rN/Qk9P
a6KR9bBOdHFFul3DEP/uPqWV9TvV5tJhP3J2RbfS79WljFy/lFIwvJvfQHYEjMt4
/gq8yTSUgJ8zmgJQ1sgOKH1FzJd4EdAMquSYbElkc35jX8gggUNOUcwsIfJBnu41
SC51JQruNT256zse76o8Dx3lSHiz5c6luZyJnZWWt6xWtfGEGMnckpn6cVvcbbgq
eWqmttgE2QTpgYoYUVcX/XFtsmZVTu05r8MZoqje5rgW9nEvvW+3M+eT5h0M9eGQ
bIT3D3tdXB2XWCjUWqxpZscFwyumGu7vdykBKLhMVR3nEpFfORnH+534vwi49fjz
WnN6fXAZZLPnGtEdWXNgs9JtgI5UheAQbcA3FT+M3maa88V2JrETLps405NYp6hJ
6msbS/AmV/eSilRmbGVj9TfKHb/BVHNYwVQ0Bu/QN2YQNQ9olOpIxXgKr6Y4tKZt
wTOMiCxZrnDQneQOTnW0NAMCAwEAAaNjMGEwHQYDVR0OBBYEFNLiS6YezH2bWiZ3
TNbkQzMoBBB2MB8GA1UdIwQYMBaAFNLiS6YezH2bWiZ3TNbkQzMoBBB2MA8GA1Ud
EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEBCwUAA4ICAQAQ
cWIFOusSLKizqcqMsbMIY/Jzy0Tq5jzeOAQEQBztu7eJb208SG2EJtD6ylBQCF8t
FCxUbvWNrly1MJoMSXdn3uMz3kLKNQa6RENckwA1UuYZpdhvTUtmPun9QqFPJdqm
oi0paOVut9q3dplDy6MUknGN4tNWp2ZDfyvom3mUMfYGEO/FCWTy8eFd6cHRE9bw
tHkcX5r7GpDHH5vKXOF/deMp1Xgep5ZTasL13YwPiYgctst91pEfdcztjHW0mQNT
ZH/TUQDgs2UCjcvyeOlgoZixWOpkf1Qyje15k9qMb89/5hdarxvAQbG2BezQtzyk
bbCu1MQa2DBdAKbhQxas/DPSvSkA/y8v+hiovTWtPKErPnQqZqVy59KUTBWj8ZAj
5dkDVjBvUcsJ/6zHv0X9puEnIDZ8pK+Xn9LbcbPE7Nf1ikDyOqHmLmhGfWlEGvoD
3Q8f8zUySZ40mfqtVhc7OYqA66Q9quNQ4VBESVNiEJ/LuWHRXe74KqFdggsQqtS6
UQQgw5lFnKHZ9pk2VlKzgpkmd5fLMOhcHWQbsah9TFOuW5vEhWGHNhGCyGouWTzD
mkwlPS8arj/ymUn6t/oiwSOA6GbjQLnTXvoAjdBxnukQlNY6TUDk+lSQw0qfZGIA
xZywUgRbLZH8TFUnuEQps35XnWrY8rrXVj9+9h0B4g==
-----END CERTIFICATE----- -----END CERTIFICATE-----

+ 29
- 22
_dev/boring/client_rsa.crt View File

@@ -1,24 +1,31 @@
-----BEGIN CERTIFICATE----- -----BEGIN CERTIFICATE-----
MIID/jCCAeYCAQEwDQYJKoZIhvcNAQELBQAwRTELMAkGA1UEBhMCQVUxEzARBgNV
BAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0
ZDAeFw0xODAyMTMyMDU0MjZaFw0xOTAyMTMyMDU0MjZaMEUxCzAJBgNVBAYTAkFV
MRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRz
IFB0eSBMdGQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDD1li7/35Q
C/T6FACSbsd0WlImu42i6w96wfngAfEgbz5Ip+IA2rJ8G5LNHTYYCpr9LlmhY6zm
soHgAkff6XwUnZaetX01UmGP4CD4D3UumkR1uKY4bCSNImm53SZgelOznpsqAWKE
zosMrDcOAJKJSN411KwVzWysfRCPyxvmLETzU9KHFCJ1oY3t1HzYIAqpHv9sMSst
dNHW3X7bWEAVKCQMKO+rWe/wAhE4iTVdlRi02oRoRWSVj41+nk6jI8KJNq70stHc
QSST0A7SUacPYKJWqJRhP1pZ6k4G3ZVE8332az7jvcN1uGGjERZoUbZxGB+mbMCC
GJwnwnNiI6/hAgMBAAEwDQYJKoZIhvcNAQELBQADggIBADNr6QMl57CdHEzNwc2M
CSZsuOLakp8YiovVDOXJ/p/lykUIIcR1rI1iNfb8oOFTZmrndGZVAh76EExdMHYG
m+4Vr2+z/73AZwvhhnLhftOKFFwkdjCfXouPlkc/zmhOORakIFGlLZFkuZRY6k2D
Q8uIt7E5uXSVl11A1LxN5X8lhK2G4lxJZuj1AqEFj9QD44Qy+MdgX38lzGCEXd8c
Y5K8zLJGbgXgYaFxqd0bImfjgjj82+Mui0OTV5PcRlczJX08ygKjcoAMVyvPHu72
3zzxvoNcqUrvbptVvg9c7FSOpK95YZOe1LiyqZCwNJQl4fPRE++XQ4zDNdyiAp76
a6BQg/M8gOpV/VBMTsNDr/yP/7eBqkfvU7jLfz7wKMDdcjeZnKom42f+/XOLEo6E
hyDuHGdQh10bZD/Ukcs69+pA3ioic1A8pQzAElH3IuDBsMJg30x8tACLKNcUY8BE
2eJgrCxWcvq88DeAT03W9AVpFZA8ZQUR3SHCquMBFogsmUDDMN+CoC0u5dBwHP+O
9rmWOXn8gp/zBCKGwemgVV5vSNzJs7z3aoqIiAABl56LBaXxjKzRmXoB/SyUW5zl
1zy4SQTE6SJYqqU6h2yRdT8n0oWN3AMy0VxbJTRq32kdYJVQK9cLKVqpxtCCtPnN
3lV+HDsj7k+AJjHiu1F4O+sp
MIIFSjCCAzKgAwIBAgIUCKk2npLYEX2Z3Ceu1CwSKK50j04wDQYJKoZIhvcNAQEL
BQAwfTELMAkGA1UEBhMCRlIxDTALBgNVBAgMBFBBQ0ExFzAVBgNVBAcMDkNhZ25l
cyBzdXIgTWVyMSIwIAYDVQQLDBlDZXJ0IFRlc3RpbmcgT3JnYW5pemF0aW9uMSIw
IAYDVQQDDBlDZXJ0IFRlc3RpbmcgT3JnYW5pemF0aW9uMB4XDTE5MDIyMjAwNDMz
MloXDTQ2MDcwOTAwNDMzMlowfTELMAkGA1UEBhMCRlIxDTALBgNVBAgMBFBBQ0Ex
FzAVBgNVBAcMDkNhZ25lcyBzdXIgTWVyMSIwIAYDVQQLDBlDZXJ0IFRlc3Rpbmcg
T3JnYW5pemF0aW9uMSIwIAYDVQQDDBlDZXJ0IFRlc3RpbmcgT3JnYW5pemF0aW9u
MIIBIDANBgkqhkiG9w0BAQEFAAOCAQ0AMIIBCAKCAQEA0DQrWlCfijylutz28aTB
T9WDnvBWpJ535t/Clt4o2nv3Cp6JxvUVzYkdKuaLR295gyEBx9JSHiZxPiJoPVfp
wigmG0R9HvByAG5rhaQbQt99npoBaHMps1i12VxxFy1yaqZW6mrwrHMfV716rZ2M
AzWx7UfhutloBYeeluiziDWUSEuGeJG7kHdvUtGYlbRd/ElFWHOfAQ7Oc8UUjEHW
sorkqciqyAERV/H9hr5Rap/J/ERcFC8bNecS4t1Yh98WgIun/MbcBKQzo1LsWmOQ
dmaMBoG8g1mYRbNap8G/+aQbjfRi1zN0yaW1wtlLoBJmNgLjwYgaS/5Uey5NZMdb
NwIBA6OBwzCBwDAJBgNVHRMEAjAAMBEGCWCGSAGG+EIBAQQEAwIFoDAxBglghkgB
hvhCAQ0EJBYiQ2VydCBUZXN0aW5nIEludGVybWVkaWF0ZSAtIENsaWVudDAdBgNV
HQ4EFgQULlrGFPbxwz525ywQYPs72P7YnL4wHwYDVR0jBBgwFoAU0uJLph7MfZta
JndM1uRDMygEEHYwDgYDVR0PAQH/BAQDAgXgMB0GA1UdJQQWMBQGCCsGAQUFBwMC
BggrBgEFBQcDBDANBgkqhkiG9w0BAQsFAAOCAgEAwLc+Xg5Fyfbgu5iFs1y7E0Et
4E/5lF0A4iqDVX3e7/upoUIBFZFv2PlAqIlhQ49NgGlIfrBlwEijZJ9kgVmUcKDS
UrqBvKUn+99dTC8Zn/Py9ofLNcJy+qNJg4TpbpBxXaP1MXdZYXdYkGtyyPIGo31U
oHibNLQDCtKFMoEPCvFuCBtJgyT46l5KN7VQCA0ZDm84fVmIgEEOXWwz0mDIhGWm
hDhmqONznl0+aHirqJxsBaplBaFVV1N02ksR53sPPy/UfDsAD3Fpp8R1DAMEyy0o
kTqm8QINVL961YT1Y/oI+GlypjPq9cL0dEHdxwu6gyCHPMMGGGIDHmLoqJJuj/Kr
/T08jhtDv8D7e9m3wfSW/RqHKE31Yy21SXv/gpcHGunwzDoj/QUvRl/xTjJfx+S8
2NHxSU8QOdexhJumsNFJe8kH8cRJMCMB8/hfiBpI0QANkUBJ1aaa/p7vZuEKJm+/
85m3Yz+zn58/Bube06z6QzFeR8Edi+6hXk4/WoHltgXiNowD3d4xI48sPWEbe+QZ
6u60sEdpY2a+3Xwt9m9R2R+sGP3QyDFd9GVaUPt21TeeLdfS3kPqwO2k+UXB8nV3
Yh1Hvyx67u0tX3wBVe40CNaAu7iW+e4aXjksG2dxk71lNq5CHJCOtbRK4LUArjy5
cw4KAXWoaR8YIC3BWgg=
-----END CERTIFICATE----- -----END CERTIFICATE-----

+ 28
- 25
_dev/boring/client_rsa.key View File

@@ -1,27 +1,28 @@
MIIEpAIBAAKCAQEAw9ZYu/9+UAv0+hQAkm7HdFpSJruNousPesH54AHxIG8+SKfi
ANqyfBuSzR02GAqa/S5ZoWOs5rKB4AJH3+l8FJ2WnrV9NVJhj+Ag+A91LppEdbim
OGwkjSJpud0mYHpTs56bKgFihM6LDKw3DgCSiUjeNdSsFc1srH0Qj8sb5ixE81PS
hxQidaGN7dR82CAKqR7/bDErLXTR1t1+21hAFSgkDCjvq1nv8AIROIk1XZUYtNqE
aEVklY+Nfp5OoyPCiTau9LLR3EEkk9AO0lGnD2CiVqiUYT9aWepOBt2VRPN99ms+
473DdbhhoxEWaFG2cRgfpmzAghicJ8JzYiOv4QIDAQABAoIBADKcbZhAYjt7q5cJ
nlA5svA9+2cpJ2SITRrTkKk0t0VDmpwaTw0bd+8dDSZXO0ihTQbLeLx9zwxb67ah
wEN8yuVlCK0BiFdEcBRHvx18mTMvCSxHSSXhxNx4nUw8fBOI6aLNBZqoevaJjmP7
CctjmHtESrEswkBsM36sX6BZxF8Kc4Q5Znuxqksnl6HNoxnjhmygJmYCFTToiTHa
f2HWKBiZfgfxX7WEuHer3h6nmBbBCOX1/hcipBMBBVIqFl1ZSIF/B3lR8UV4/X+a
SNMqggOqkEIuHKkSCKo1lNxEPP2p54EHrKkjepoqMzIFuYnn4qWesMznpmy+zBGB
6PCjfzUCgYEA92etvRVQjBx8dHTSiyCNbS1ELgiCkzar8PGH+ytTIaj/TTF1LfAi
UYRp5MtOKmQXxE3IRLDF8P8rEKC06aV12hVwhd2xfHjje+KZkwWZ2PIj+GbK7f1r
MvKN5eE0NhGiSvu5SiFuks/SV8Qc4StFPmiWf33XKvJuAWNkCu+bUZsCgYEAyqQL
nVNKTlgHNKDJKMHi/buZt8wtwGGXCxcv+w88PmEC0OCbH/V2niCPLvFmK1xDXpru
k7z9FTc+QeasEMtxY/Gcs3IgUzxOHxAL7cn6KBM44uDhpIcv3BFWtR053acVU6S4
IKuijWIJNJEk2qksgQTX7Mv/xq2uXvfZqajdKjMCgYEA3x+5F9s+Pm5+a4TkUSc1
hS4a3C0+ncfjv7QEwCftnGDOhu7A0IJOYRg7bGVShHaq3JaNtC19BwEJ9MALCOD5
bYqCZahvpmNcPeE6Qdb+TiLq/96sy4AOiu8nvBejv9Ode2SUUd/e2jbla9Ppe8VL
eKJYgHicchYb0dKyag54FFsCgYEAuToEB9W3aS9bvsZtuZyooSfXFcND2sMZrqCO
Uh2WAqroSQfVo/vaZiX623z62A2o4xQZmd+5MqhhdxmkFGHyDtouU3SxiYPpIMmp
Lb1etT0E1ZWbi6mqnK0YpcrGNw5gFynMyMg6eKOxKGS33EuhC3ni6Wd7MB9X8ST6
x/M73jMCgYBBge3/ugnZPE78TDL3DdefrjeYFaKhVc622eimS/MEPbkbdxh8azTM
LAoibwDU1NC8/3MfOBYMe6Qklu3kjexOJrfdo0Z7Khgd9F8A4tKwslUndSSlAfKF
2rjfqabVMZMLZ2XEbA4W5JTfaZS4YYGcrjY7+i7OsnSxoYG2sb+xlQ==
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDQNCtaUJ+KPKW6
3PbxpMFP1YOe8Faknnfm38KW3ijae/cKnonG9RXNiR0q5otHb3mDIQHH0lIeJnE+
Img9V+nCKCYbRH0e8HIAbmuFpBtC332emgFocymzWLXZXHEXLXJqplbqavCscx9X
vXqtnYwDNbHtR+G62WgFh56W6LOINZRIS4Z4kbuQd29S0ZiVtF38SUVYc58BDs5z
xRSMQdayiuSpyKrIARFX8f2GvlFqn8n8RFwULxs15xLi3ViH3xaAi6f8xtwEpDOj
UuxaY5B2ZowGgbyDWZhFs1qnwb/5pBuN9GLXM3TJpbXC2UugEmY2AuPBiBpL/lR7
Lk1kx1s3AgEDAoIBAQCKzXI8Nb+xfcPR6KShGIDf460UoDnDFE/vP9cPPsXm/U9c
abEvTg6JBhNx7weE9PuswKvajDa+xEt+wZrTj/EsGsQSLai/Svaq9EeubWeB6lO/
EVZFohvM5c6Q6EtkyPbxxDnxnKBy92o6flHJE7KsznaeL+vR5kVZBRRkmyJazS4s
Z5rbrN9AhSIfyHs9GCQGgsXT6HMsyoJYFastwQ2qj+9L2ypcM8TW+KGzGfJipoJb
l/N/8WHb4ZumA67lfWq4v5JTA5qAUKcfPszEBrUfQ34Tk+73Iiov9f7SXPYxWxVJ
g9PuzfewvJrp6CPv+/mKNt8PmBYkaXlnyjr9tCwLAoGBAPjAVZapQVuIqftcOZtf
Re9fAV9Vvv1FEO8bKJeIsPDlRkdg+TfTMgxhZU0I3P4XdEj7Fa87w4wkA6GkIrOO
W9/usPOYzSdTP5aVEsdGbT8yD2vTST7Aw/GESKTRJA/Fe1PIb5Nz3OijyTusvFE+
XSR3EXb1myX+2rFS0Wbiz2U5AoGBANZFWoeFzREnBcDG60RayjiTg71E1/T4zhvU
e/w+71FNbLZXBrNqgV20F73xOme/Mb13yr+YgXxIEQfFtR6hRxZ8u1jndEzw66Jf
YfHt7EGVceMV2pdP4md5ebebEj7qICfXPxF9IZicwZG3QMR5u0tvnx40iNMWhW0M
rY4FabPvAoGBAKXVjmRw1j0FxqeS0RI/g/TqAOo5Kf4uC0oSGw+wdfXuLtpApiU3
drLrmN4F6Klk+DCnY8on17LCrRZtbHe0PT/0dfe7M2+M1Q8ODITZniohX503hinV
1/ZYMG3gwrUuUjfa9Qz36JsX230d0uDUPhhPYPn5EhlUkcuMi5nsikN7AoGBAI7Y
5wUD3gtvWSsvR4LnMXsNAn4t5U37NBKNp/1/SjYznc7kryJHAOkiun6g0Zp/dn5P
3H+7AP2FYK/ZI2nA2g790jtE+DNLR8GU6/aenYEOS+y5PGTf7ET7pnpnYX9GwBqP
f2D+FmW91mEk1dhRJ4efv2l4WzdkWPNdyQlY8SKfAoGAEIWmowo7EpbR5Boxc2o3
Tl0JbGi1CNJAHtDjEJCd1OxKrMUrK07hOeEKF6y8K/WBuQhFvI2pu16oT4sMal9Z
mEiJdJAFErefPLQGomHLXfq9mDEY13Ug/xAd9aMyYcubIg5XjAqLMrB60HcrLr7Q
2hMCSDdVP2V/F3QVh8DEirE=
-----END PRIVATE KEY-----

+ 2
- 2
_dev/boring/run.sh View File

@@ -2,7 +2,7 @@
set -e set -e


/boringssl/build/tool/bssl client -grease -min-version tls1.3 -max-version tls1.3 \ /boringssl/build/tool/bssl client -grease -min-version tls1.3 -max-version tls1.3 \
-tls13-variant draft22 -session-out /session -connect "$@" < /httpreq.txt
-session-out /session -connect "$@" < /httpreq.txt
exec /boringssl/build/tool/bssl client -grease -min-version tls1.3 -max-version tls1.3 \ exec /boringssl/build/tool/bssl client -grease -min-version tls1.3 -max-version tls1.3 \
-tls13-variant draft22 -session-in /session -connect "$@" < /httpreq.txt
-session-in /session -connect "$@" < /httpreq.txt



+ 8
- 3
_dev/boring/server.sh View File

@@ -6,22 +6,27 @@ set -x
bssl server \ bssl server \
-key rsa.pem \ -key rsa.pem \
-min-version tls1.2 -max-version tls1.3 \ -min-version tls1.2 -max-version tls1.3 \
-tls13-draft22-variant \
-accept 1443 -loop -www 2>&1 & -accept 1443 -loop -www 2>&1 &


# ECDSA # ECDSA
bssl server \ bssl server \
-key ecdsa.pem \ -key ecdsa.pem \
-min-version tls1.2 -max-version tls1.3 \ -min-version tls1.2 -max-version tls1.3 \
-tls13-draft22-variant \
-accept 2443 -loop -www 2>&1 & -accept 2443 -loop -www 2>&1 &


# Require client authentication (with ECDSA) # Require client authentication (with ECDSA)
bssl server \ bssl server \
-key ecdsa.pem \ -key ecdsa.pem \
-min-version tls1.2 -max-version tls1.3 \ -min-version tls1.2 -max-version tls1.3 \
-tls13-draft22-variant \
-accept 6443 -loop -www \ -accept 6443 -loop -www \
-require-any-client-cert -debug 2>&1 & -require-any-client-cert -debug 2>&1 &


# ECDSA and SIDH/P503-X25519
bssl server \
-key ecdsa.pem \
-curves X25519-SIDHp503:X25519:P-256:P-384:P-521 \
-min-version tls1.2 -max-version tls1.3 \
-accept 7443 -loop -www \
-debug 2>&1 &

wait wait

+ 5149
- 0
_dev/boring/sidh_ff433815b51c34496bb6bea13e73e29e5c278238.patch
File diff suppressed because it is too large
View File


+ 83
- 29
_dev/interop_test_runner View File

@@ -10,11 +10,17 @@ import time
# Checks if TLS 1.3 was negotiated # Checks if TLS 1.3 was negotiated
RE_PATTERN_HELLO_TLS_13_NORESUME = "^.*Hello TLS 1.3 \(draft .*\) _o/$|^.*Hello TLS 1.3 _o/$" RE_PATTERN_HELLO_TLS_13_NORESUME = "^.*Hello TLS 1.3 \(draft .*\) _o/$|^.*Hello TLS 1.3 _o/$"
# Checks if TLS 1.3 was resumed # Checks if TLS 1.3 was resumed
RE_PATTERN_HELLO_TLS_13_RESUME = "Hello TLS 1.3 \(draft .*\) \[resumed\] _o/"
RE_PATTERN_HELLO_TLS_13_RESUME = "Hello TLS 1.3 \[resumed\] _o/"
# Checks if 0-RTT was used and NOT confirmed # Checks if 0-RTT was used and NOT confirmed
RE_PATTERN_HELLO_0RTT = "^.*Hello TLS 1.3 .*\[resumed\] \[0-RTT\] _o/$" RE_PATTERN_HELLO_0RTT = "^.*Hello TLS 1.3 .*\[resumed\] \[0-RTT\] _o/$"
# Checks if 0-RTT was used and confirmed # Checks if 0-RTT was used and confirmed
RE_PATTERN_HELLO_0RTT_CONFIRMED = "^.*Hello TLS 1.3 .*\[resumed\] \[0-RTT confirmed\] _o/$" RE_PATTERN_HELLO_0RTT_CONFIRMED = "^.*Hello TLS 1.3 .*\[resumed\] \[0-RTT confirmed\] _o/$"
# ALPN
RE_PATTERN_ALPN = "ALPN protocol: npn_proto$"
# Successful TLS establishement from TRIS
RE_TRIS_ALL_PASSED = ".*All handshakes passed.*"
# TLS handshake from BoringSSL with SIDH/P503-X25519
RE_BORINGSSL_P503 = "ECDHE curve: X25519-SIDHp503"


class Docker(object): class Docker(object):
''' Utility class used for starting/stoping servers and clients during tests''' ''' Utility class used for starting/stoping servers and clients during tests'''
@@ -46,14 +52,13 @@ class RegexSelfTest(unittest.TestCase):
''' Ensures that those regexe's actually work ''' ''' Ensures that those regexe's actually work '''


LINE_HELLO_TLS ="\nsomestuff\nHello TLS 1.3 _o/\nsomestuff" LINE_HELLO_TLS ="\nsomestuff\nHello TLS 1.3 _o/\nsomestuff"
LINE_HELLO_DRAFT_TLS="\nsomestuff\nHello TLS 1.3 (draft 22) _o/\nsomestuff"

LINE_HELLO_RESUMED ="\nsomestuff\nHello TLS 1.3 (draft 22) [resumed] _o/\nsomestuff"
LINE_HELLO_MIXED ="\nsomestuff\nHello TLS 1.3 (draft 22) _o/\nHello TLS 1.3 (draft 22) [resumed] _o/\nsomestuff"
LINE_HELLO_TLS_12 ="\nsomestuff\nHello TLS 1.2 (draft 22) [resumed] _o/\nsomestuff"
LINE_HELLO_TLS_13_0RTT="\nsomestuff\nHello TLS 1.3 (draft 22) [resumed] [0-RTT] _o/\nsomestuff"
LINE_HELLO_TLS_13_0RTT_CONFIRMED="\nsomestuff\nHello TLS 1.3 (draft 22) [resumed] [0-RTT confirmed] _o/\nsomestuff"
LINE_HELLO_DRAFT_TLS="\nsomestuff\nHello TLS 1.3 (draft 23) _o/\nsomestuff"


LINE_HELLO_RESUMED ="\nsomestuff\nHello TLS 1.3 [resumed] _o/\nsomestuff"
LINE_HELLO_MIXED ="\nsomestuff\nHello TLS 1.3 (draft 23) _o/\nHello TLS 1.3 (draft 23) [resumed] _o/\nsomestuff"
LINE_HELLO_TLS_12 ="\nsomestuff\nHello TLS 1.2 (draft 23) [resumed] _o/\nsomestuff"
LINE_HELLO_TLS_13_0RTT="\nsomestuff\nHello TLS 1.3 (draft 23) [resumed] [0-RTT] _o/\nsomestuff"
LINE_HELLO_TLS_13_0RTT_CONFIRMED="\nsomestuff\nHello TLS 1.3 (draft 23) [resumed] [0-RTT confirmed] _o/\nsomestuff"
def test_regexes(self): def test_regexes(self):
self.assertIsNotNone( self.assertIsNotNone(
re.search(RE_PATTERN_HELLO_TLS_13_NORESUME, RegexSelfTest.LINE_HELLO_TLS, re.MULTILINE)) re.search(RE_PATTERN_HELLO_TLS_13_NORESUME, RegexSelfTest.LINE_HELLO_TLS, re.MULTILINE))
@@ -190,7 +195,7 @@ class InteropClient(object):
self.d = Docker() self.d = Docker()
self.server = self.d.run_server( self.server = self.d.run_server(
self.SERVER_NAME, self.SERVER_NAME,
ports={ '1443/tcp': 1443, '2443/tcp': 2443, '6443/tcp': 6443},
ports={ '1443/tcp': 1443, '2443/tcp': 2443, '6443/tcp': 6443, '7443/tcp': 7443},
entrypoint="/server.sh") entrypoint="/server.sh")


@classmethod @classmethod
@@ -204,20 +209,37 @@ class InteropClient(object):


# Actual test definition # Actual test definition


# TRIS as a server
class InteropServer_BoringSSL(
InteropServer,
ServerNominalMixin,
ServerClientAuthMixin,
unittest.TestCase
): CLIENT_NAME = "tls-tris:boring"
# TRIS as a server, BoringSSL as a client
class InteropServer_BoringSSL(InteropServer, ServerNominalMixin, ServerClientAuthMixin, unittest.TestCase):


class InteropServer_PicoTLS(
InteropServer,
ServerNominalMixin,
ServerZeroRttMixin,
unittest.TestCase
): CLIENT_NAME = "tls-tris:picotls"
CLIENT_NAME = "tls-tris:boring"

def test_ALPN(self):
'''
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.
'''
res = self.d.run_client(self.CLIENT_NAME, self.server_ip+":1443 "+'-alpn-protos npn_proto')
self.assertEqual(res[0], 0)
self.assertIsNotNone(re.search(RE_PATTERN_ALPN, res[1], re.MULTILINE))

def test_SIDH(self):
'''
Connects to TRIS server listening on 7443 and tries to perform key agreement with SIDH/P503-X25519
'''
res = self.d.run_client(self.CLIENT_NAME, self.server_ip+":7443 "+'-curves X25519-SIDHp503')
self.assertEqual(res[0], 0)
self.assertIsNotNone(re.search(RE_BORINGSSL_P503, res[1], re.MULTILINE))
self.assertIsNotNone(re.search(RE_PATTERN_HELLO_TLS_13_NORESUME, res[1], re.MULTILINE))

# PicoTLS doesn't seem to implement draft-23 correctly. It will
# be enabled when draft-28 is implemented.
# class InteropServer_PicoTLS(
# InteropServer,
# ServerNominalMixin,
# ServerZeroRttMixin,
# unittest.TestCase
# ): CLIENT_NAME = "tls-tris:picotls"


class InteropServer_NSS( class InteropServer_NSS(
InteropServer, InteropServer,
@@ -226,13 +248,29 @@ class InteropServer_NSS(
unittest.TestCase unittest.TestCase
): CLIENT_NAME = "tls-tris:tstclnt" ): CLIENT_NAME = "tls-tris:tstclnt"


# TRIS as a client
class InteropClient_BoringSSL(
InteropClient,
ClientNominalMixin,
ClientClientAuthMixin,
unittest.TestCase
): SERVER_NAME = "boring-localserver"
# TRIS as a client, BoringSSL as a server
class InteropClient_BoringSSL(InteropClient, ClientNominalMixin, ClientClientAuthMixin, unittest.TestCase):

SERVER_NAME = "boring-localserver"

def test_SIDH(self):
'''
Connects to BoringSSL server listening on 7443 and tries to perform key agreement with SIDH/P503-X25519
'''
res = self.d.run_client(self.CLIENT_NAME, '-rsa=false -ecdsa=true -groups X25519-SIDHp503 ' + self.server_ip+":7443")
self.assertEqual(res[0], 0)
self.assertIsNotNone(re.search(RE_TRIS_ALL_PASSED, res[1], re.MULTILINE))

def test_SIDH_TLSv12(self):
'''
Connects to TRIS server listening on 7443 and tries to perform key agreement with SIDH/P503-X25519
This connection will be over TLSv12 and hence it should fall back to X25519
'''
res = self.d.run_client(self.CLIENT_NAME, '-tls_version=1.2 -rsa=false -ecdsa=true -groups X25519-SIDHp503:P-256 -ciphers TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 ' + self.server_ip+":7443")
self.assertEqual(res[0], 0)
# Go doesn't provide API to get NamedGroup ID, but boringssl on port 7443 accepts only TLS1.2
# so if handshake was successful, then that's all what we need
self.assertIsNotNone(re.search(RE_TRIS_ALL_PASSED, res[1], re.MULTILINE))


class InteropClient_NSS( class InteropClient_NSS(
InteropClient, InteropClient,
@@ -250,5 +288,21 @@ 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_SIDH(self):
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
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()

+ 0
- 60
_dev/patches/88253a956a753213617d95af3f42a23a78798473.patch View File

@@ -1,63 +0,0 @@
From 88253a956a753213617d95af3f42a23a78798473 Mon Sep 17 00:00:00 2001
From: Filippo Valsorda <filippo@cloudflare.com>
Date: Mon, 28 Nov 2016 05:24:21 +0000
Subject: [PATCH] net/http: attach TLSConnContextKey to the request Context

Change-Id: Ic59c84f992c829dc7da741b128dd6899366fa1d2
src/net/http/request.go | 4 +++-
src/net/http/server.go | 12 ++++++++++++
2 files changed, 15 insertions(+), 1 deletion(-)

diff --git a/src/net/http/request.go b/src/net/http/request.go
index 13f367c1a8..b2827ff123 100644
+++ b/src/net/http/request.go
@@ -275,7 +275,9 @@ type Request struct {
// was received. This field is not filled in by ReadRequest.
// The HTTP server in this package sets the field for
// TLS-enabled connections before invoking a handler;
- // otherwise it leaves the field nil.
+ // otherwise it leaves the field nil. The value is fixed
+ // at the state of the connection immediately after Handshake,
+ // for an immediate value use TLSConnContextKey.
// This field is ignored by the HTTP client.
TLS *tls.ConnectionState
diff --git a/src/net/http/server.go b/src/net/http/server.go
index 2fa8ab23d8..b0542cdbc3 100644
+++ b/src/net/http/server.go
@@ -223,6 +223,12 @@ var (
// the local address the connection arrived on.
// The associated value will be of type net.Addr.
LocalAddrContextKey = &contextKey{"local-addr"}
+
+ // TLSConnContextKey is a context key. It can be used in
+ // HTTP handlers with context.WithValue to access the
+ // underlying *tls.Conn being served. If the connection
+ // is not TLS, the key is not set.
+ TLSConnContextKey = &contextKey{"tls-conn"}
)
// A conn represents the server side of an HTTP connection.
@@ -969,6 +975,9 @@ func (c *conn) readRequest(ctx context.Context) (w *response, err error) {
delete(req.Header, "Host")
ctx, cancelCtx := context.WithCancel(ctx)
+ if tlsConn, ok := c.rwc.(*tls.Conn); ok {
+ ctx = context.WithValue(ctx, TLSConnContextKey, tlsConn)
+ }
req.ctx = ctx
req.RemoteAddr = c.remoteAddr
req.TLS = c.tlsState
@@ -3161,6 +3170,9 @@ func (h initNPNRequest) ServeHTTP(rw ResponseWriter, req *Request) {
if req.RemoteAddr == "" {
req.RemoteAddr = h.c.RemoteAddr().String()
}
+ if req.ctx != nil && req.ctx.Value(TLSConnContextKey) == nil {
+ req.ctx = context.WithValue(req.ctx, TLSConnContextKey, h.c)
+ }
h.h.ServeHTTP(rw, req)
}

+ 7
- 0
_dev/tls_examples/Makefile View File

@@ -0,0 +1,7 @@
# Used to build examples. Must be compiled from current directory and after "make build-all"

build:
GOROOT=../GOROOT/linux_amd64 go build ems_client.go

run:
./ems_client google.com:443

+ 75
- 0
_dev/tls_examples/ems_client.go View File

@@ -0,0 +1,75 @@
package main

import (
"bufio"
"crypto/tls"
"flag"
"fmt"
"io"
"net/http"
"net/url"
"os"
)

var tlsVersionToName = map[string]uint16{
"tls10": tls.VersionTLS10,
"tls11": tls.VersionTLS11,
"tls12": tls.VersionTLS12,
"tls13": tls.VersionTLS13,
}

// Usage client args host:port
func main() {
var version string
var addr string
var enableEMS bool
var resume bool
var config tls.Config
var cache tls.ClientSessionCache
cache = tls.NewLRUClientSessionCache(0)
flag.StringVar(&version, "version", "tls12", "Version of TLS to use")
flag.BoolVar(&enableEMS, "m", false, "Enable EMS")
flag.BoolVar(&resume, "r", false, "Attempt Resumption")
flag.Parse()
config.MinVersion = tlsVersionToName[version]
config.MaxVersion = tlsVersionToName[version]
config.InsecureSkipVerify = true
config.UseExtendedMasterSecret = !enableEMS
config.ClientSessionCache = cache
var iters int
if resume {
iters = 2
} else {
iters = 1
}
addr = flag.Arg(0)
for ; iters > 0; iters-- {
conn, err := tls.Dial("tcp", addr, &config)
if err != nil {
fmt.Println("Error %s", err)
os.Exit(1)
}
var req http.Request
var response *http.Response
req.Method = "GET"
req.URL, err = url.Parse("https://" + addr + "/")
if err != nil {
fmt.Println("Failed to parse url")
os.Exit(1)
}
req.Write(conn)
reader := bufio.NewReader(conn)
response, err = http.ReadResponse(reader, nil)
if err != nil {
fmt.Println("HTTP problem")
fmt.Println(err)
os.Exit(1)
}
io.Copy(os.Stdout, response.Body)
conn.Close()
if resume && iters == 2 {
fmt.Println("Attempting resumption")
}
}
os.Exit(0)
}

+ 1
- 0
_dev/tris-localserver/Dockerfile 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 /


+ 7
- 6
_dev/tris-localserver/runner.sh View File

@@ -1,10 +1,11 @@
#!/bin/sh #!/bin/sh


./tris-localserver -b 0.0.0.0:1443 -palg=rsa -rtt0=n 2>&1 & # first port: ECDSA (and no 0-RTT)
./tris-localserver -b 0.0.0.0:2443 -palg=ecdsa -rtt0=a 2>&1 & # second port: RSA (and accept 0-RTT but not offer it)
./tris-localserver -b 0.0.0.0:3443 -palg=ecdsa -rtt0=o 2>&1 & # third port: offer and reject 0-RTT
./tris-localserver -b 0.0.0.0:4443 -palg=ecdsa -rtt0=oa 2>&1 & # fourth port: offer and accept 0-RTT
./tris-localserver -b 0.0.0.0:5443 -palg=ecdsa -rtt0=oa -rtt0ack 2>&1 & # fifth port: offer and accept 0-RTT but confirm
./tris-localserver -b 0.0.0.0:6443 -palg=rsa -cliauth 2>&1 & # sixth port: RSA with required client authentication
./tris-localserver -b 0.0.0.0:1443 -cert=rsa -rtt0=n 2>&1 & # first port: ECDSA (and no 0-RTT)
./tris-localserver -b 0.0.0.0:2443 -cert=ecdsa -rtt0=a 2>&1 & # second port: RSA (and accept 0-RTT but not offer it)
./tris-localserver -b 0.0.0.0:3443 -cert=ecdsa -rtt0=o 2>&1 & # third port: offer and reject 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:6443 -cert=rsa -cliauth 2>&1 & # sixth port: RSA with required client authentication
./tris-localserver -b 0.0.0.0:7443 -cert=ecdsa -pq=c & # Enables support for both - post-quantum and classical KEX algorithms


wait wait

+ 200
- 151
_dev/tris-localserver/server.go View File

@@ -2,15 +2,17 @@ package main


import ( import (
"crypto/tls" "crypto/tls"
"encoding/hex"
"crypto/x509"
"errors"
"flag" "flag"
"fmt" "fmt"
"io"
"io/ioutil"
"log" "log"
"net/http" "net/http"
"os" "os"
"strings"
"syscall"
"time" "time"
"crypto/x509"
) )


type ZeroRTT_t int type ZeroRTT_t int
@@ -18,149 +20,204 @@ type PubKeyAlgo_t int


// Bitset // Bitset
const ( const (
ZeroRTT_None ZeroRTT_t = 0
ZeroRTT_Offer = 1 << 0
ZeroRTT_Accept = 1 << 1
)

const (
PubKeyRSA PubKeyAlgo_t = iota
PubKeyECDSA
ZeroRTT_None ZeroRTT_t = 0
ZeroRTT_Offer = 1 << 0
ZeroRTT_Accept = 1 << 1
) )


type server struct { type server struct {
Address string
ZeroRTT ZeroRTT_t
PubKey PubKeyAlgo_t
ClientAuthMethod tls.ClientAuthType
Address string
ZeroRTT ZeroRTT_t
TLS tls.Config
} }


var tlsVersionToName = map[uint16]string{ var tlsVersionToName = map[uint16]string{
tls.VersionTLS10: "1.0",
tls.VersionTLS11: "1.1",
tls.VersionTLS12: "1.2",
tls.VersionTLS13: "1.3",
tls.VersionTLS13Draft18: "1.3 (draft 18)",
tls.VersionTLS13Draft21: "1.3 (draft 21)",
tls.VersionTLS13Draft22: "1.3 (draft 22)",
tls.VersionTLS10: "1.0",
tls.VersionTLS11: "1.1",
tls.VersionTLS12: "1.2",
tls.VersionTLS13: "1.3",
} }


func NewServer() *server { func NewServer() *server {
s := new(server)
s.ClientAuthMethod = tls.NoClientCert
s.ZeroRTT = ZeroRTT_None
s.Address = "0.0.0.1:443"
return s
s := new(server)
s.ZeroRTT = ZeroRTT_None
s.Address = "0.0.0.0:443"
s.TLS = tls.Config{
GetConfigForClient: func(*tls.ClientHelloInfo) (*tls.Config, error) {
// If we send the first flight too fast, NSS sends empty early data.
time.Sleep(500 * time.Millisecond)
return nil, nil
},
MaxVersion: tls.VersionTLS13,
ClientAuth: tls.NoClientCert,
}

return s
} }


func (s *server) start() {
cert, err := tls.X509KeyPair([]byte(ecdsaCert), []byte(ecdsaKey))
if s.PubKey == PubKeyRSA {
cert, err = tls.X509KeyPair([]byte(rsaCert), []byte(rsaKey))
func enablePQ(s *server, enableDefault bool) {
var pqGroups = []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...)
} }
if err != nil {
log.Fatal(err)
}
var Max0RTTDataSize uint32
if ((s.ZeroRTT&ZeroRTT_Offer) == ZeroRTT_Offer) {
Max0RTTDataSize = 100 * 1024
s.TLS.CurvePreferences = append(s.TLS.CurvePreferences, pqGroups...)
}

func (s *server) start() {
var err error
if (s.ZeroRTT & ZeroRTT_Offer) == ZeroRTT_Offer {
s.TLS.Max0RTTDataSize = 100 * 1024
} }
var keyLogWriter io.Writer
if keyLogFile := os.Getenv("SSLKEYLOGFILE"); keyLogFile != "" { if keyLogFile := os.Getenv("SSLKEYLOGFILE"); keyLogFile != "" {
keyLogWriter, err = os.OpenFile(keyLogFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0600)
s.TLS.KeyLogWriter, err = os.OpenFile(keyLogFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0600)
if err != nil { if err != nil {
log.Fatalf("Cannot open keylog file: %v", err) log.Fatalf("Cannot open keylog file: %v", err)
} }
log.Println("Enabled keylog") log.Println("Enabled keylog")
} }


clientCAs := x509.NewCertPool()
clientCAs.AppendCertsFromPEM([]byte(rsaCa_client))
s.TLS.ClientCAs = x509.NewCertPool()
s.TLS.ClientCAs.AppendCertsFromPEM([]byte(rsaCa_client))
s.TLS.Accept0RTTData = ((s.ZeroRTT & ZeroRTT_Accept) == ZeroRTT_Accept)
s.TLS.NextProtos = []string{"npn_proto"}


httpServer := &http.Server{ httpServer := &http.Server{
Addr: s.Address,
TLSConfig: &tls.Config{
Certificates: []tls.Certificate{cert},
Max0RTTDataSize: Max0RTTDataSize,
Accept0RTTData: (s.ZeroRTT&ZeroRTT_Accept) == ZeroRTT_Accept,
KeyLogWriter: keyLogWriter,
GetConfigForClient: func(*tls.ClientHelloInfo) (*tls.Config, error) {
// If we send the first flight too fast, NSS sends empty early data.
time.Sleep(500 * time.Millisecond)
return nil, nil
},
MaxVersion: tls.VersionTLS13,
ClientAuth: s.ClientAuthMethod,
ClientCAs: clientCAs,
},
Addr: s.Address,
TLSConfig: &s.TLS,
} }
log.Fatal(httpServer.ListenAndServeTLS("", "")) log.Fatal(httpServer.ListenAndServeTLS("", ""))
} }


func main() {
// setServerCertificateFromArgs sets server certificate from an argument provided by the caller. Possible values
// for arg_cert:
// * "rsa": sets hardcoded RSA keypair
// * "ecdsa": sets hardcoded ECDSA keypair
// * FILE1:FILE2: Uses private key from FILE1 and public key from FILE2. Both must be in PEM format. FILE2 can
// be single certificate or certificate chain.
// * nil: fallbacks to "rsa"
//
// Function generate a panic in case certificate can't be correctly set
func (s *server) setServerCertificateFromArgs(arg_cert *string) {
var certStr, keyStr []byte
var cert tls.Certificate
var err error


s := NewServer()
if arg_cert == nil {
// set rsa by default
certStr, keyStr = []byte(rsaCert), []byte(rsaKey)
} else {
switch *arg_cert {
case "rsa":
certStr, keyStr = []byte(rsaCert), []byte(rsaKey)
case "ecdsa":
certStr, keyStr = []byte(ecdsaCert), []byte(ecdsaKey)
default:
files := strings.Split(*arg_cert, ":")
if len(files) != 2 {
err = errors.New("Wrong format provided after -cert.")
goto err
}
keyStr, err = ioutil.ReadFile(files[0])
if err != nil {
goto err
}
certStr, err = ioutil.ReadFile(files[1])
if err != nil {
goto err
}
}
}


arg_addr := flag.String("b" , "0.0.0.0:443", "Address:port used for binding")
arg_palg := flag.String("palg", "rsa", "Public algorithm to use: rsa or ecdsa")
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)")
flag.Parse()
cert, err = tls.X509KeyPair(certStr, keyStr)
if err != nil {
goto err
}


s.Address=*arg_addr
s.TLS.Certificates = []tls.Certificate{cert}
err:
if err != nil {
// Not possible to proceed really
log.Fatal(err)
panic(err)
}
}


if *arg_palg == "ecdsa" {
s.PubKey = PubKeyECDSA
}
func charsToString(ca []int8) string {
s := make([]byte, len(ca))
var lens int
for ; lens < len(ca); lens++ {
if ca[lens] == 0 {
break
}
s[lens] = uint8(ca[lens])
}
return string(s[0:lens])
}


if *arg_zerortt == "a" {
s.ZeroRTT = ZeroRTT_Accept
} else if *arg_zerortt == "o" {
s.ZeroRTT = ZeroRTT_Offer
} else if *arg_zerortt == "oa" {
s.ZeroRTT = ZeroRTT_Offer | ZeroRTT_Accept
}
func main() {


if *arg_clientauth {
s.ClientAuthMethod = tls.RequireAndVerifyClientCert
}
s := NewServer()


http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
tlsConn := r.Context().Value(http.TLSConnContextKey).(*tls.Conn)
arg_addr := flag.String("b", "0.0.0.0:443", "Address:port used for binding")
arg_cert := flag.String("cert", "rsa", "Public algorithm to use:\nOptions [rsa, ecdsa, PrivateKeyFile:CertificateChainFile]")
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_pq := flag.String("pq", "", "Enable quantum-resistant algorithms [c: Support classical and Quantum-Resistant, q: Enable Quantum-Resistant only]")
flag.Parse()


with0RTT := ""
if !tlsConn.ConnectionState().HandshakeConfirmed {
with0RTT = " [0-RTT]"
}
if *arg_confirm || r.URL.Path == "/confirm" {
if err := tlsConn.ConfirmHandshake(); err != nil {
log.Fatal(err)
}
if with0RTT != "" {
with0RTT = " [0-RTT confirmed]"
}
if !tlsConn.ConnectionState().HandshakeConfirmed {
panic("HandshakeConfirmed false after ConfirmHandshake")
}
}
s.Address = *arg_addr


resumed := ""
if r.TLS.DidResume {
resumed = " [resumed]"
}
s.setServerCertificateFromArgs(arg_cert)


http2 := ""
if r.ProtoMajor == 2 {
http2 = " [HTTP/2]"
}
if *arg_zerortt == "a" {
s.ZeroRTT = ZeroRTT_Accept
} else if *arg_zerortt == "o" {
s.ZeroRTT = ZeroRTT_Offer
} else if *arg_zerortt == "oa" {
s.ZeroRTT = ZeroRTT_Offer | ZeroRTT_Accept
}


fmt.Fprintf(w, "<!DOCTYPE html><p>Hello TLS %s%s%s%s _o/\n", tlsVersionToName[r.TLS.Version], resumed, with0RTT, http2)
})
if *arg_clientauth {
s.TLS.ClientAuth = tls.RequireAndVerifyClientCert
}

if *arg_pq == "c" {
enablePQ(s, true)
} else if *arg_pq == "q" {
enablePQ(s, false)
}


http.HandleFunc("/ch", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/plain")
fmt.Fprintf(w, "Client Hello packet (%d bytes):\n%s", len(r.TLS.ClientHello), hex.Dump(r.TLS.ClientHello))
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
tlsConn := r.Context().Value(http.TLSConnContextKey).(*tls.Conn)
var buf syscall.Utsname

err := syscall.Uname(&buf)
if err != nil {
panic("Uname error\n")
}
node := charsToString(buf.Nodename[:])
release := charsToString(buf.Release[:])
version := charsToString(buf.Version[:])
machine := charsToString(buf.Machine[:])
sysname := charsToString(buf.Sysname[:])
_ = tlsConn
fmt.Fprintf(w, "<!DOCTYPE html><body style=\"font-family: courier\">Node\t\t\t\t: %s</br>Version\t\t\t\t: %s</br>Release\t\t\t\t: %s</br>Machine\t\t\t\t: %s</br>Sysname\t\t\t\t: %s</br><pre>", node, version, release, machine, sysname)
fmt.Fprintf(w, `
▄████▄ ██▀███ ▓██ ██▓ ██▓███ ▄▄▄█████▓ ▒█████
▒██▀ ▀█ ▓██ ▒ ██▒▒██ ██▒▓██░ ██▒▓ ██▒ ▓▒▒██▒ ██▒
▒▓█ ▄ ▓██ ░▄█ ▒ ▒██ ██░▓██░ ██▓▒▒ ▓██░ ▒░▒██░ ██▒
▒▓▓▄ ▄██▒▒██▀▀█▄ ░ ▐██▓░▒██▄█▓▒ ▒░ ▓██▓ ░ ▒██ ██░
▒ ▓███▀ ░░██▓ ▒██▒ ░ ██▒▓░▒██▒ ░ ░ ▒██▒ ░ ░ ████▓▒░
░ ░▒ ▒ ░░ ▒▓ ░▒▓░ ██▒▒▒ ▒▓▒░ ░ ░ ▒ ░░ ░ ▒░▒░▒░
░ ▒ ░▒ ░ ▒░▓██ ░▒░ ░▒ ░ ░ ░ ▒ ▒░
░ ░░ ░ ▒ ▒ ░░ ░░ ░ ░ ░ ░ ▒
░ ░ ░ ░ ░ ░ ░
░ ░ ░
`)
fmt.Fprintf(w, "</pre></body></html>")
}) })


s.start() s.start()
@@ -212,50 +269,40 @@ ClMLEiNJQ0OMxAIaRtb2RehD4q3OWlpWf6joJ36PRBqL8T5+f2x6Tg3c64UR+QPX
98UcCQHHdEhm7y2z5Z2Wt0B48tZ+UAxDEoEwMghNyw7wUD79IRlXGYypBnXaMuLX 98UcCQHHdEhm7y2z5Z2Wt0B48tZ+UAxDEoEwMghNyw7wUD79IRlXGYypBnXaMuLX
46aGxbsSQ7Rfg62Co3JG7vo+eJd0AoZHrtFUnfM8V70IFzMBZnSwRslHRJe56Q== 46aGxbsSQ7Rfg62Co3JG7vo+eJd0AoZHrtFUnfM8V70IFzMBZnSwRslHRJe56Q==
-----END CERTIFICATE-----` -----END CERTIFICATE-----`
rsaCa_client = `-----BEGIN CERTIFICATE-----
MIIFYDCCA0igAwIBAgIJAPpBgIvtQb1EMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV
BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX
aWRnaXRzIFB0eSBMdGQwHhcNMTgwMjEzMjAxNjA3WhcNMTkwMjEzMjAxNjA3WjBF
MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50
ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC
CgKCAgEAr4xgdmB4DaEh8zRFmg/1ZxYhQZMUP0iQX/Y8nDWxNlcd42p3TgpY1biz
jrq58ln9Om4U/GAn2RmtBAynSBXIlR5oVa44JeMM8Ka8R/dMKyHpF0Nj2EJB9unb
TC33PfzOlnKQxATwevnnhI6tGluWmwvxXUi7WnX0di+nQg9HrIVom3KrmRr2/41y
g497ccYUuNnKE6sewGdGzw045oWZpMDA2Us+MFo1IywOurjaM9bueRhPTcIiQ8RE
h7qb+FRwfxaj9ynZA2PCM7WMSSWCiZJV0uj/pshYF2lvtJcJef4dhwnsYBpc+mgx
2q9qcUBeo3ZHbi1/PRqjwSmcW3yY5cQRbpYp6xFmgmX3oHQkVXS0UlpNVZ+morcS
HEpaK8b76fCFcL5yFsAJkPPfny1IKU+CfaVq60dM/mxbEW6J4mZT/uAiqrCilMC+
FyiATCZur8Ks7p47eZy700DllLod7gWTiuZTgHeQFVoX+jxbCZKlFn5Xspu8ALoK
Mla/q83mICRVy3+eMUsD7DNvoWYpCAYy/oMk0VWfrQ48JkCGbBW2PW/dU2nmqVhY
/11rurkr+1TUvYodnajANtXvUjW1DPOLb4dES4Qc4b7Fw8eFXrARhl5mXiL5HFKR
/VnRshiJ+QwTVkxl+KkZHEm/WS8QD+Zd8leAxh9MCoaU/XrBUBkCAwEAAaNTMFEw
HQYDVR0OBBYEFKUinuD1xRvcNd2Wti/PnBJp7On1MB8GA1UdIwQYMBaAFKUinuD1
xRvcNd2Wti/PnBJp7On1MA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQAD
ggIBAJdJrNBftqkTs2HyuJ3x5RIsTxYh85hJYwNOdFLyzVG6HER9jRCnvmNTjG0O
I5wz5hQvDpwXs4BCCXHQZrTLAi3BEjq3AjrmR/XeGHulbWh3eh8LVu7MiLRgt+Ys
GnL2IaERrbkje24nCCMNPbI3fGDQEhTIYmmX8RJp+5BOJgCycKk6pFgfrjJv2C+d
78pcjlYII6M4vPnr/a08M49Bq6b5ADvIfe5G2KrUvD/+vwoAwv6d/daymHCQ2rY5
kmdVk9VUp3Q4uKoeej4ENJSAUNTV7oTu346oc7q9sJffB5OltqbrE7ichak7lL+v
EjArZHElAhKNFXRZViCMvGDs+7JztqbsfT8Xb6Z27e+WyudB2bOUGm3hKuTIl06D
bA7yUskwEhmkd1CJqO5RLEJjKitOqe6Ye0/GsmPQNDK8GvyXTyGQK5OqBuzEexF0
mlPoIhpSVH3K9SkRTTHvvcbdYlaQLi6gKq2uhbk4PnS2nfBtXqYIy9mxcgBJzLiB
/ydfLcf3GClwgvO1JHp6qAl4CO7oe8jqHpoGuznwi1aqkTyNkQWh0OXq3MS+dyqB
2yXFCFIeKCx18TE1OtuTD3ppBDjpyd0o/a6kYR3FDmdks/J33bGwLsLH3lbN6VjF
PNfNkaE1tfkpSGYsuT1DPxX8aAT4JLUfZ1Si6iO+E0Sj9LXA
ecdsaCert = `-----BEGIN CERTIFICATE-----
MIIBbTCCAROgAwIBAgIQZCsHZcs5ZkzV+zC2E6j5RzAKBggqhkjOPQQDAjASMRAw
DgYDVQQKEwdBY21lIENvMB4XDTE2MDkyNDE3NTE1OFoXDTI2MDkyMjE3NTE1OFow
EjEQMA4GA1UEChMHQWNtZSBDbzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABDTO
B3IyzjYfKCp2HWy+P3QHxhdBT4AUGYgwTiSEj5phumPIahFNcOSWptN0UzlZvJdN
MMjVmrFYK/FjF4abkNKjSzBJMA4GA1UdDwEB/wQEAwIFoDATBgNVHSUEDDAKBggr
BgEFBQcDATAMBgNVHRMBAf8EAjAAMBQGA1UdEQQNMAuCCWxvY2FsaG9zdDAKBggq
hkjOPQQDAgNIADBFAiEAp9W157PM1IadPBc33Cbj7vaFvp+rXs/hSuMCzP8pgV8C
IHCswo1qiC0ZjQmWsBlmz5Zbp9rOorIzBYmGRhRdNs3j
rsaCa_client = `-----BEGIN CERTIFICATE-----
MIIF6zCCA9OgAwIBAgIUC4U4HlbkVMrKKTFK0mNrMFDpRskwDQYJKoZIhvcNAQEL
BQAwfTELMAkGA1UEBhMCRlIxDTALBgNVBAgMBFBBQ0ExFzAVBgNVBAcMDkNhZ25l
cyBzdXIgTWVyMSIwIAYDVQQLDBlDZXJ0IFRlc3RpbmcgT3JnYW5pemF0aW9uMSIw
IAYDVQQDDBlDZXJ0IFRlc3RpbmcgT3JnYW5pemF0aW9uMB4XDTE5MDIyMjAwNDIz
OVoXDTQ2MDcwOTAwNDIzOVowfTELMAkGA1UEBhMCRlIxDTALBgNVBAgMBFBBQ0Ex
FzAVBgNVBAcMDkNhZ25lcyBzdXIgTWVyMSIwIAYDVQQLDBlDZXJ0IFRlc3Rpbmcg
T3JnYW5pemF0aW9uMSIwIAYDVQQDDBlDZXJ0IFRlc3RpbmcgT3JnYW5pemF0aW9u
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA0z+DMLb7YIMFFNZpn+ve
NdT7GL9DPyV9ZWSHpUuDyme6Og6Mp5IpCLlKjNXtizX5aQ9xQ746slt70fivSV/r
tiEtayZkcwS7zHtc5f+U/S0hR1q5Zh3DaLQH9diSeuNFQN5pg7zQT5csJFlxf6EB
j/ioSBC+J1E8A2FAh0qDq+TvPPyZEEjcJy0oBuNHUnkC3rwjt24DAUI26rN/Qk9P
a6KR9bBOdHFFul3DEP/uPqWV9TvV5tJhP3J2RbfS79WljFy/lFIwvJvfQHYEjMt4
/gq8yTSUgJ8zmgJQ1sgOKH1FzJd4EdAMquSYbElkc35jX8gggUNOUcwsIfJBnu41
SC51JQruNT256zse76o8Dx3lSHiz5c6luZyJnZWWt6xWtfGEGMnckpn6cVvcbbgq
eWqmttgE2QTpgYoYUVcX/XFtsmZVTu05r8MZoqje5rgW9nEvvW+3M+eT5h0M9eGQ
bIT3D3tdXB2XWCjUWqxpZscFwyumGu7vdykBKLhMVR3nEpFfORnH+534vwi49fjz
WnN6fXAZZLPnGtEdWXNgs9JtgI5UheAQbcA3FT+M3maa88V2JrETLps405NYp6hJ
6msbS/AmV/eSilRmbGVj9TfKHb/BVHNYwVQ0Bu/QN2YQNQ9olOpIxXgKr6Y4tKZt
wTOMiCxZrnDQneQOTnW0NAMCAwEAAaNjMGEwHQYDVR0OBBYEFNLiS6YezH2bWiZ3
TNbkQzMoBBB2MB8GA1UdIwQYMBaAFNLiS6YezH2bWiZ3TNbkQzMoBBB2MA8GA1Ud
EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEBCwUAA4ICAQAQ
cWIFOusSLKizqcqMsbMIY/Jzy0Tq5jzeOAQEQBztu7eJb208SG2EJtD6ylBQCF8t
FCxUbvWNrly1MJoMSXdn3uMz3kLKNQa6RENckwA1UuYZpdhvTUtmPun9QqFPJdqm
oi0paOVut9q3dplDy6MUknGN4tNWp2ZDfyvom3mUMfYGEO/FCWTy8eFd6cHRE9bw
tHkcX5r7GpDHH5vKXOF/deMp1Xgep5ZTasL13YwPiYgctst91pEfdcztjHW0mQNT
ZH/TUQDgs2UCjcvyeOlgoZixWOpkf1Qyje15k9qMb89/5hdarxvAQbG2BezQtzyk
bbCu1MQa2DBdAKbhQxas/DPSvSkA/y8v+hiovTWtPKErPnQqZqVy59KUTBWj8ZAj
5dkDVjBvUcsJ/6zHv0X9puEnIDZ8pK+Xn9LbcbPE7Nf1ikDyOqHmLmhGfWlEGvoD
3Q8f8zUySZ40mfqtVhc7OYqA66Q9quNQ4VBESVNiEJ/LuWHRXe74KqFdggsQqtS6
UQQgw5lFnKHZ9pk2VlKzgpkmd5fLMOhcHWQbsah9TFOuW5vEhWGHNhGCyGouWTzD
mkwlPS8arj/ymUn6t/oiwSOA6GbjQLnTXvoAjdBxnukQlNY6TUDk+lSQw0qfZGIA
xZywUgRbLZH8TFUnuEQps35XnWrY8rrXVj9+9h0B4g==
-----END CERTIFICATE-----` -----END CERTIFICATE-----`
ecdsaKey = `-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIFdhO7IW5UIwpB1e2Vunm9QyKvUHWcVwGfLjhpOajuR7oAoGCCqGSM49
AwEHoUQDQgAENM4HcjLONh8oKnYdbL4/dAfGF0FPgBQZiDBOJISPmmG6Y8hqEU1w
5Jam03RTOVm8l00wyNWasVgr8WMXhpuQ0g==
ecdsaCert = ``
ecdsaKey = ``
) )

+ 198
- 123
_dev/tris-testclient/client.go View File

@@ -3,6 +3,7 @@ package main
import ( import (
"crypto/tls" "crypto/tls"
"crypto/x509" "crypto/x509"
"errors"
"flag" "flag"
"fmt" "fmt"
"io" "io"
@@ -12,11 +13,10 @@ import (
) )


var tlsVersionToName = map[uint16]string{ var tlsVersionToName = map[uint16]string{
tls.VersionTLS10: "1.0",
tls.VersionTLS11: "1.1",
tls.VersionTLS12: "1.2",
tls.VersionTLS13: "1.3",
tls.VersionTLS13Draft18: "1.3 (draft 18)",
tls.VersionTLS10: "1.0",
tls.VersionTLS11: "1.1",
tls.VersionTLS12: "1.2",
tls.VersionTLS13: "1.3",
} }


var cipherSuiteIdToName = map[uint16]string{ var cipherSuiteIdToName = map[uint16]string{
@@ -27,28 +27,56 @@ var cipherSuiteIdToName = map[uint16]string{
tls.TLS_CHACHA20_POLY1305_SHA256: "TLS_CHACHA20_POLY1305_SHA256", tls.TLS_CHACHA20_POLY1305_SHA256: "TLS_CHACHA20_POLY1305_SHA256",
} }


type Client struct {
KeyLogWriter io.Writer
failed uint
client_cert tls.Certificate
client_certpool *x509.CertPool
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",
uint16(tls.CurveP521): "P-521",
} }


func (c *Client) run(addr string, version, cipherSuite uint16) {
fmt.Printf("TLS %s with %s\n", tlsVersionToName[version], cipherSuiteIdToName[cipherSuite])
tls_config := &tls.Config{
InsecureSkipVerify: true,
MinVersion: version,
MaxVersion: version,
CipherSuites: []uint16{cipherSuite},
KeyLogWriter: c.KeyLogWriter,
Certificates: []tls.Certificate{c.client_cert},
RootCAs: c.client_certpool,
func getIDByName(m map[uint16]string, name string) (uint16, error) {
for key, value := range m {
if value == name {
return key, nil
}
} }
con, err := tls.Dial("tcp", addr, tls_config)
return 0, errors.New("Unknown value")
}

var failed uint

type Client struct {
TLS tls.Config
addr string
}

func NewClient() *Client {
var c Client
c.TLS.InsecureSkipVerify = true
return &c
}

func (c *Client) clone() *Client {
var clone Client
clone.TLS = *c.TLS.Clone()
clone.addr = c.addr
return &clone
}

func (c *Client) setMinMaxTLS(ver uint16) {
c.TLS.MinVersion = ver
c.TLS.MaxVersion = ver
}

func (c *Client) run() {
fmt.Printf("TLS %s with %s\n", tlsVersionToName[c.TLS.MinVersion], cipherSuiteIdToName[c.TLS.CipherSuites[0]])

con, err := tls.Dial("tcp", c.addr, &c.TLS)
if err != nil { if err != nil {
fmt.Printf("handshake failed: %v\n\n", err) fmt.Printf("handshake failed: %v\n\n", err)
c.failed++
failed++
return return
} }
defer con.Close() defer con.Close()
@@ -56,7 +84,7 @@ func (c *Client) run(addr string, version, cipherSuite uint16) {
_, err = con.Write([]byte("GET / HTTP/1.1\r\nHost: localhost\r\n\r\n")) _, err = con.Write([]byte("GET / HTTP/1.1\r\nHost: localhost\r\n\r\n"))
if err != nil { if err != nil {
fmt.Printf("Write failed: %v\n\n", err) fmt.Printf("Write failed: %v\n\n", err)
c.failed++
failed++
return return
} }


@@ -66,33 +94,45 @@ func (c *Client) run(addr string, version, cipherSuite uint16) {
// is received right after reading data (observed with NSS selfserv). // is received right after reading data (observed with NSS selfserv).
if !(n > 0 && err == io.EOF) && err != nil { if !(n > 0 && err == io.EOF) && err != nil {
fmt.Printf("Read failed: %v\n\n", err) fmt.Printf("Read failed: %v\n\n", err)
c.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")
} }


func result() {
if failed > 0 {
log.Fatalf("Failed handshakes: %d\n", failed)
} else {
fmt.Println("All handshakes passed")
}
}

// Usage client args host:port
func main() { func main() {
var keylog_file string
var keylog_file, tls_version, named_groups, named_ciphers 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(&tls_version, "tls_version", "1.3", "TLS version to use")
flag.StringVar(&named_groups, "groups", "X25519:P-256:P-384:P-521", "NamedGroups IDs to use")
flag.StringVar(&named_ciphers, "ciphers", "TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384", "Named cipher IDs to use")
flag.Parse() flag.Parse()
if flag.NArg() != 1 { if flag.NArg() != 1 {
flag.Usage() flag.Usage()
os.Exit(1) os.Exit(1)
} }
addr := flag.Arg(0)
if !strings.Contains(addr, ":") {
addr += ":443"

client := NewClient()
client.addr = flag.Arg(0)
if !strings.Contains(client.addr, ":") {
client.addr += ":443"
} }


client := Client{}
if keylog_file == "" { if keylog_file == "" {
keylog_file = os.Getenv("SSLKEYLOGFILE") keylog_file = os.Getenv("SSLKEYLOGFILE")
} }
@@ -101,129 +141,163 @@ func main() {
if err != nil { if err != nil {
log.Fatalf("Cannot open keylog file: %v", err) log.Fatalf("Cannot open keylog file: %v", err)
} }
client.KeyLogWriter = keylog_writer
client.TLS.KeyLogWriter = keylog_writer
log.Println("Enabled keylog") log.Println("Enabled keylog")
} }


if client_auth { if client_auth {
var err error var err error
client.client_cert, err = tls.X509KeyPair([]byte(client_crt), []byte(client_key))
client_cert, err := tls.X509KeyPair([]byte(client_crt), []byte(client_key))
if err != nil { if err != nil {
panic("Can't load client certificate") panic("Can't load client certificate")
} }


client.client_certpool = x509.NewCertPool()
if !client.client_certpool.AppendCertsFromPEM([]byte(client_ca)) {
client.TLS.Certificates = []tls.Certificate{client_cert}
client.TLS.RootCAs = x509.NewCertPool()
if !client.TLS.RootCAs.AppendCertsFromPEM([]byte(client_ca)) {
panic("Can't load client CA cert") panic("Can't load client CA cert")
} }
} }


if enable_rsa { if enable_rsa {
// Sanity check: TLS 1.2 with the mandatory cipher suite from RFC 5246 // Sanity check: TLS 1.2 with the mandatory cipher suite from RFC 5246
client.run(addr, tls.VersionTLS12, tls.TLS_RSA_WITH_AES_128_CBC_SHA)
c := client.clone()
c.TLS.CipherSuites = []uint16{tls.TLS_RSA_WITH_AES_128_CBC_SHA}
c.setMinMaxTLS(tls.VersionTLS12)
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)
client.run(addr, tls.VersionTLS12, tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256)
c := client.clone()
c.TLS.CipherSuites = []uint16{tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256}
c.setMinMaxTLS(tls.VersionTLS12)
c.run()
} }


// Set requested DH groups
client.TLS.CurvePreferences = []tls.CurveID{}
for _, ng := range strings.Split(named_groups, ":") {
id, err := getIDByName(namedGroupsToName, ng)
if err != nil {
panic("Wrong group name provided")
}
client.TLS.CurvePreferences = append(client.TLS.CurvePreferences, tls.CurveID(id))
}


client.run(addr, tls.VersionTLS13, tls.TLS_CHACHA20_POLY1305_SHA256)
client.run(addr, tls.VersionTLS13, tls.TLS_AES_128_GCM_SHA256)
client.run(addr, tls.VersionTLS13, tls.TLS_AES_256_GCM_SHA384)
// Perform TLS handshake with each each requested CipherSuite
tlsID, err := getIDByName(tlsVersionToName, tls_version)
if err != nil {
panic("Unknown TLS version")
}
for _, cn := range strings.Split(named_ciphers, ":") {
id, err := getIDByName(cipherSuiteIdToName, cn)
if err != nil {
panic("Wrong cipher name provided")
}
client.setMinMaxTLS(tlsID)
client.TLS.CipherSuites = []uint16{id}
client.run()
}


// TODO test other kex methods besides X25519, like MTI secp256r1 // TODO test other kex methods besides X25519, like MTI secp256r1
// TODO limit supported groups? // TODO limit supported groups?


if client.failed > 0 {
log.Fatalf("Failed handshakes: %d\n", client.failed)
} else {
fmt.Println("All handshakes passed")
}
result()
} }


const ( const (
client_ca = `-----BEGIN CERTIFICATE----- client_ca = `-----BEGIN CERTIFICATE-----
MIIFYDCCA0igAwIBAgIJAPpBgIvtQb1EMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV
BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX
aWRnaXRzIFB0eSBMdGQwHhcNMTgwMjEzMjAxNjA3WhcNMTkwMjEzMjAxNjA3WjBF
MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50
ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC
CgKCAgEAr4xgdmB4DaEh8zRFmg/1ZxYhQZMUP0iQX/Y8nDWxNlcd42p3TgpY1biz
jrq58ln9Om4U/GAn2RmtBAynSBXIlR5oVa44JeMM8Ka8R/dMKyHpF0Nj2EJB9unb
TC33PfzOlnKQxATwevnnhI6tGluWmwvxXUi7WnX0di+nQg9HrIVom3KrmRr2/41y
g497ccYUuNnKE6sewGdGzw045oWZpMDA2Us+MFo1IywOurjaM9bueRhPTcIiQ8RE
h7qb+FRwfxaj9ynZA2PCM7WMSSWCiZJV0uj/pshYF2lvtJcJef4dhwnsYBpc+mgx
2q9qcUBeo3ZHbi1/PRqjwSmcW3yY5cQRbpYp6xFmgmX3oHQkVXS0UlpNVZ+morcS
HEpaK8b76fCFcL5yFsAJkPPfny1IKU+CfaVq60dM/mxbEW6J4mZT/uAiqrCilMC+
FyiATCZur8Ks7p47eZy700DllLod7gWTiuZTgHeQFVoX+jxbCZKlFn5Xspu8ALoK
Mla/q83mICRVy3+eMUsD7DNvoWYpCAYy/oMk0VWfrQ48JkCGbBW2PW/dU2nmqVhY
/11rurkr+1TUvYodnajANtXvUjW1DPOLb4dES4Qc4b7Fw8eFXrARhl5mXiL5HFKR
/VnRshiJ+QwTVkxl+KkZHEm/WS8QD+Zd8leAxh9MCoaU/XrBUBkCAwEAAaNTMFEw
HQYDVR0OBBYEFKUinuD1xRvcNd2Wti/PnBJp7On1MB8GA1UdIwQYMBaAFKUinuD1
xRvcNd2Wti/PnBJp7On1MA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQAD
ggIBAJdJrNBftqkTs2HyuJ3x5RIsTxYh85hJYwNOdFLyzVG6HER9jRCnvmNTjG0O
I5wz5hQvDpwXs4BCCXHQZrTLAi3BEjq3AjrmR/XeGHulbWh3eh8LVu7MiLRgt+Ys
GnL2IaERrbkje24nCCMNPbI3fGDQEhTIYmmX8RJp+5BOJgCycKk6pFgfrjJv2C+d
78pcjlYII6M4vPnr/a08M49Bq6b5ADvIfe5G2KrUvD/+vwoAwv6d/daymHCQ2rY5
kmdVk9VUp3Q4uKoeej4ENJSAUNTV7oTu346oc7q9sJffB5OltqbrE7ichak7lL+v
EjArZHElAhKNFXRZViCMvGDs+7JztqbsfT8Xb6Z27e+WyudB2bOUGm3hKuTIl06D
bA7yUskwEhmkd1CJqO5RLEJjKitOqe6Ye0/GsmPQNDK8GvyXTyGQK5OqBuzEexF0
mlPoIhpSVH3K9SkRTTHvvcbdYlaQLi6gKq2uhbk4PnS2nfBtXqYIy9mxcgBJzLiB
/ydfLcf3GClwgvO1JHp6qAl4CO7oe8jqHpoGuznwi1aqkTyNkQWh0OXq3MS+dyqB
2yXFCFIeKCx18TE1OtuTD3ppBDjpyd0o/a6kYR3FDmdks/J33bGwLsLH3lbN6VjF
PNfNkaE1tfkpSGYsuT1DPxX8aAT4JLUfZ1Si6iO+E0Sj9LXA
MIIF6zCCA9OgAwIBAgIUC4U4HlbkVMrKKTFK0mNrMFDpRskwDQYJKoZIhvcNAQEL
BQAwfTELMAkGA1UEBhMCRlIxDTALBgNVBAgMBFBBQ0ExFzAVBgNVBAcMDkNhZ25l
cyBzdXIgTWVyMSIwIAYDVQQLDBlDZXJ0IFRlc3RpbmcgT3JnYW5pemF0aW9uMSIw
IAYDVQQDDBlDZXJ0IFRlc3RpbmcgT3JnYW5pemF0aW9uMB4XDTE5MDIyMjAwNDIz
OVoXDTQ2MDcwOTAwNDIzOVowfTELMAkGA1UEBhMCRlIxDTALBgNVBAgMBFBBQ0Ex
FzAVBgNVBAcMDkNhZ25lcyBzdXIgTWVyMSIwIAYDVQQLDBlDZXJ0IFRlc3Rpbmcg
T3JnYW5pemF0aW9uMSIwIAYDVQQDDBlDZXJ0IFRlc3RpbmcgT3JnYW5pemF0aW9u
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA0z+DMLb7YIMFFNZpn+ve
NdT7GL9DPyV9ZWSHpUuDyme6Og6Mp5IpCLlKjNXtizX5aQ9xQ746slt70fivSV/r
tiEtayZkcwS7zHtc5f+U/S0hR1q5Zh3DaLQH9diSeuNFQN5pg7zQT5csJFlxf6EB
j/ioSBC+J1E8A2FAh0qDq+TvPPyZEEjcJy0oBuNHUnkC3rwjt24DAUI26rN/Qk9P
a6KR9bBOdHFFul3DEP/uPqWV9TvV5tJhP3J2RbfS79WljFy/lFIwvJvfQHYEjMt4
/gq8yTSUgJ8zmgJQ1sgOKH1FzJd4EdAMquSYbElkc35jX8gggUNOUcwsIfJBnu41
SC51JQruNT256zse76o8Dx3lSHiz5c6luZyJnZWWt6xWtfGEGMnckpn6cVvcbbgq
eWqmttgE2QTpgYoYUVcX/XFtsmZVTu05r8MZoqje5rgW9nEvvW+3M+eT5h0M9eGQ
bIT3D3tdXB2XWCjUWqxpZscFwyumGu7vdykBKLhMVR3nEpFfORnH+534vwi49fjz
WnN6fXAZZLPnGtEdWXNgs9JtgI5UheAQbcA3FT+M3maa88V2JrETLps405NYp6hJ
6msbS/AmV/eSilRmbGVj9TfKHb/BVHNYwVQ0Bu/QN2YQNQ9olOpIxXgKr6Y4tKZt
wTOMiCxZrnDQneQOTnW0NAMCAwEAAaNjMGEwHQYDVR0OBBYEFNLiS6YezH2bWiZ3
TNbkQzMoBBB2MB8GA1UdIwQYMBaAFNLiS6YezH2bWiZ3TNbkQzMoBBB2MA8GA1Ud
EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEBCwUAA4ICAQAQ
cWIFOusSLKizqcqMsbMIY/Jzy0Tq5jzeOAQEQBztu7eJb208SG2EJtD6ylBQCF8t
FCxUbvWNrly1MJoMSXdn3uMz3kLKNQa6RENckwA1UuYZpdhvTUtmPun9QqFPJdqm
oi0paOVut9q3dplDy6MUknGN4tNWp2ZDfyvom3mUMfYGEO/FCWTy8eFd6cHRE9bw
tHkcX5r7GpDHH5vKXOF/deMp1Xgep5ZTasL13YwPiYgctst91pEfdcztjHW0mQNT
ZH/TUQDgs2UCjcvyeOlgoZixWOpkf1Qyje15k9qMb89/5hdarxvAQbG2BezQtzyk
bbCu1MQa2DBdAKbhQxas/DPSvSkA/y8v+hiovTWtPKErPnQqZqVy59KUTBWj8ZAj
5dkDVjBvUcsJ/6zHv0X9puEnIDZ8pK+Xn9LbcbPE7Nf1ikDyOqHmLmhGfWlEGvoD
3Q8f8zUySZ40mfqtVhc7OYqA66Q9quNQ4VBESVNiEJ/LuWHRXe74KqFdggsQqtS6
UQQgw5lFnKHZ9pk2VlKzgpkmd5fLMOhcHWQbsah9TFOuW5vEhWGHNhGCyGouWTzD
mkwlPS8arj/ymUn6t/oiwSOA6GbjQLnTXvoAjdBxnukQlNY6TUDk+lSQw0qfZGIA
xZywUgRbLZH8TFUnuEQps35XnWrY8rrXVj9+9h0B4g==
-----END CERTIFICATE-----` -----END CERTIFICATE-----`
client_crt = `-----BEGIN CERTIFICATE----- client_crt = `-----BEGIN CERTIFICATE-----
MIID/jCCAeYCAQEwDQYJKoZIhvcNAQELBQAwRTELMAkGA1UEBhMCQVUxEzARBgNV
BAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0
ZDAeFw0xODAyMTMyMDU0MjZaFw0xOTAyMTMyMDU0MjZaMEUxCzAJBgNVBAYTAkFV
MRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRz
IFB0eSBMdGQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDD1li7/35Q
C/T6FACSbsd0WlImu42i6w96wfngAfEgbz5Ip+IA2rJ8G5LNHTYYCpr9LlmhY6zm
soHgAkff6XwUnZaetX01UmGP4CD4D3UumkR1uKY4bCSNImm53SZgelOznpsqAWKE
zosMrDcOAJKJSN411KwVzWysfRCPyxvmLETzU9KHFCJ1oY3t1HzYIAqpHv9sMSst
dNHW3X7bWEAVKCQMKO+rWe/wAhE4iTVdlRi02oRoRWSVj41+nk6jI8KJNq70stHc
QSST0A7SUacPYKJWqJRhP1pZ6k4G3ZVE8332az7jvcN1uGGjERZoUbZxGB+mbMCC
GJwnwnNiI6/hAgMBAAEwDQYJKoZIhvcNAQELBQADggIBADNr6QMl57CdHEzNwc2M
CSZsuOLakp8YiovVDOXJ/p/lykUIIcR1rI1iNfb8oOFTZmrndGZVAh76EExdMHYG
m+4Vr2+z/73AZwvhhnLhftOKFFwkdjCfXouPlkc/zmhOORakIFGlLZFkuZRY6k2D
Q8uIt7E5uXSVl11A1LxN5X8lhK2G4lxJZuj1AqEFj9QD44Qy+MdgX38lzGCEXd8c
Y5K8zLJGbgXgYaFxqd0bImfjgjj82+Mui0OTV5PcRlczJX08ygKjcoAMVyvPHu72
3zzxvoNcqUrvbptVvg9c7FSOpK95YZOe1LiyqZCwNJQl4fPRE++XQ4zDNdyiAp76
a6BQg/M8gOpV/VBMTsNDr/yP/7eBqkfvU7jLfz7wKMDdcjeZnKom42f+/XOLEo6E
hyDuHGdQh10bZD/Ukcs69+pA3ioic1A8pQzAElH3IuDBsMJg30x8tACLKNcUY8BE
2eJgrCxWcvq88DeAT03W9AVpFZA8ZQUR3SHCquMBFogsmUDDMN+CoC0u5dBwHP+O
9rmWOXn8gp/zBCKGwemgVV5vSNzJs7z3aoqIiAABl56LBaXxjKzRmXoB/SyUW5zl
1zy4SQTE6SJYqqU6h2yRdT8n0oWN3AMy0VxbJTRq32kdYJVQK9cLKVqpxtCCtPnN
3lV+HDsj7k+AJjHiu1F4O+sp
MIIFSjCCAzKgAwIBAgIUCKk2npLYEX2Z3Ceu1CwSKK50j04wDQYJKoZIhvcNAQEL
BQAwfTELMAkGA1UEBhMCRlIxDTALBgNVBAgMBFBBQ0ExFzAVBgNVBAcMDkNhZ25l
cyBzdXIgTWVyMSIwIAYDVQQLDBlDZXJ0IFRlc3RpbmcgT3JnYW5pemF0aW9uMSIw
IAYDVQQDDBlDZXJ0IFRlc3RpbmcgT3JnYW5pemF0aW9uMB4XDTE5MDIyMjAwNDMz
MloXDTQ2MDcwOTAwNDMzMlowfTELMAkGA1UEBhMCRlIxDTALBgNVBAgMBFBBQ0Ex
FzAVBgNVBAcMDkNhZ25lcyBzdXIgTWVyMSIwIAYDVQQLDBlDZXJ0IFRlc3Rpbmcg
T3JnYW5pemF0aW9uMSIwIAYDVQQDDBlDZXJ0IFRlc3RpbmcgT3JnYW5pemF0aW9u
MIIBIDANBgkqhkiG9w0BAQEFAAOCAQ0AMIIBCAKCAQEA0DQrWlCfijylutz28aTB
T9WDnvBWpJ535t/Clt4o2nv3Cp6JxvUVzYkdKuaLR295gyEBx9JSHiZxPiJoPVfp
wigmG0R9HvByAG5rhaQbQt99npoBaHMps1i12VxxFy1yaqZW6mrwrHMfV716rZ2M
AzWx7UfhutloBYeeluiziDWUSEuGeJG7kHdvUtGYlbRd/ElFWHOfAQ7Oc8UUjEHW
sorkqciqyAERV/H9hr5Rap/J/ERcFC8bNecS4t1Yh98WgIun/MbcBKQzo1LsWmOQ
dmaMBoG8g1mYRbNap8G/+aQbjfRi1zN0yaW1wtlLoBJmNgLjwYgaS/5Uey5NZMdb
NwIBA6OBwzCBwDAJBgNVHRMEAjAAMBEGCWCGSAGG+EIBAQQEAwIFoDAxBglghkgB
hvhCAQ0EJBYiQ2VydCBUZXN0aW5nIEludGVybWVkaWF0ZSAtIENsaWVudDAdBgNV
HQ4EFgQULlrGFPbxwz525ywQYPs72P7YnL4wHwYDVR0jBBgwFoAU0uJLph7MfZta
JndM1uRDMygEEHYwDgYDVR0PAQH/BAQDAgXgMB0GA1UdJQQWMBQGCCsGAQUFBwMC
BggrBgEFBQcDBDANBgkqhkiG9w0BAQsFAAOCAgEAwLc+Xg5Fyfbgu5iFs1y7E0Et
4E/5lF0A4iqDVX3e7/upoUIBFZFv2PlAqIlhQ49NgGlIfrBlwEijZJ9kgVmUcKDS
UrqBvKUn+99dTC8Zn/Py9ofLNcJy+qNJg4TpbpBxXaP1MXdZYXdYkGtyyPIGo31U
oHibNLQDCtKFMoEPCvFuCBtJgyT46l5KN7VQCA0ZDm84fVmIgEEOXWwz0mDIhGWm
hDhmqONznl0+aHirqJxsBaplBaFVV1N02ksR53sPPy/UfDsAD3Fpp8R1DAMEyy0o
kTqm8QINVL961YT1Y/oI+GlypjPq9cL0dEHdxwu6gyCHPMMGGGIDHmLoqJJuj/Kr
/T08jhtDv8D7e9m3wfSW/RqHKE31Yy21SXv/gpcHGunwzDoj/QUvRl/xTjJfx+S8
2NHxSU8QOdexhJumsNFJe8kH8cRJMCMB8/hfiBpI0QANkUBJ1aaa/p7vZuEKJm+/
85m3Yz+zn58/Bube06z6QzFeR8Edi+6hXk4/WoHltgXiNowD3d4xI48sPWEbe+QZ
6u60sEdpY2a+3Xwt9m9R2R+sGP3QyDFd9GVaUPt21TeeLdfS3kPqwO2k+UXB8nV3
Yh1Hvyx67u0tX3wBVe40CNaAu7iW+e4aXjksG2dxk71lNq5CHJCOtbRK4LUArjy5
cw4KAXWoaR8YIC3BWgg=
-----END CERTIFICATE-----` -----END CERTIFICATE-----`
client_key = `-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAw9ZYu/9+UAv0+hQAkm7HdFpSJruNousPesH54AHxIG8+SKfi
ANqyfBuSzR02GAqa/S5ZoWOs5rKB4AJH3+l8FJ2WnrV9NVJhj+Ag+A91LppEdbim
OGwkjSJpud0mYHpTs56bKgFihM6LDKw3DgCSiUjeNdSsFc1srH0Qj8sb5ixE81PS
hxQidaGN7dR82CAKqR7/bDErLXTR1t1+21hAFSgkDCjvq1nv8AIROIk1XZUYtNqE
aEVklY+Nfp5OoyPCiTau9LLR3EEkk9AO0lGnD2CiVqiUYT9aWepOBt2VRPN99ms+
473DdbhhoxEWaFG2cRgfpmzAghicJ8JzYiOv4QIDAQABAoIBADKcbZhAYjt7q5cJ
nlA5svA9+2cpJ2SITRrTkKk0t0VDmpwaTw0bd+8dDSZXO0ihTQbLeLx9zwxb67ah
wEN8yuVlCK0BiFdEcBRHvx18mTMvCSxHSSXhxNx4nUw8fBOI6aLNBZqoevaJjmP7
CctjmHtESrEswkBsM36sX6BZxF8Kc4Q5Znuxqksnl6HNoxnjhmygJmYCFTToiTHa
f2HWKBiZfgfxX7WEuHer3h6nmBbBCOX1/hcipBMBBVIqFl1ZSIF/B3lR8UV4/X+a
SNMqggOqkEIuHKkSCKo1lNxEPP2p54EHrKkjepoqMzIFuYnn4qWesMznpmy+zBGB
6PCjfzUCgYEA92etvRVQjBx8dHTSiyCNbS1ELgiCkzar8PGH+ytTIaj/TTF1LfAi
UYRp5MtOKmQXxE3IRLDF8P8rEKC06aV12hVwhd2xfHjje+KZkwWZ2PIj+GbK7f1r
MvKN5eE0NhGiSvu5SiFuks/SV8Qc4StFPmiWf33XKvJuAWNkCu+bUZsCgYEAyqQL
nVNKTlgHNKDJKMHi/buZt8wtwGGXCxcv+w88PmEC0OCbH/V2niCPLvFmK1xDXpru
k7z9FTc+QeasEMtxY/Gcs3IgUzxOHxAL7cn6KBM44uDhpIcv3BFWtR053acVU6S4
IKuijWIJNJEk2qksgQTX7Mv/xq2uXvfZqajdKjMCgYEA3x+5F9s+Pm5+a4TkUSc1
hS4a3C0+ncfjv7QEwCftnGDOhu7A0IJOYRg7bGVShHaq3JaNtC19BwEJ9MALCOD5
bYqCZahvpmNcPeE6Qdb+TiLq/96sy4AOiu8nvBejv9Ode2SUUd/e2jbla9Ppe8VL
eKJYgHicchYb0dKyag54FFsCgYEAuToEB9W3aS9bvsZtuZyooSfXFcND2sMZrqCO
Uh2WAqroSQfVo/vaZiX623z62A2o4xQZmd+5MqhhdxmkFGHyDtouU3SxiYPpIMmp
Lb1etT0E1ZWbi6mqnK0YpcrGNw5gFynMyMg6eKOxKGS33EuhC3ni6Wd7MB9X8ST6
x/M73jMCgYBBge3/ugnZPE78TDL3DdefrjeYFaKhVc622eimS/MEPbkbdxh8azTM
LAoibwDU1NC8/3MfOBYMe6Qklu3kjexOJrfdo0Z7Khgd9F8A4tKwslUndSSlAfKF
2rjfqabVMZMLZ2XEbA4W5JTfaZS4YYGcrjY7+i7OsnSxoYG2sb+xlQ==
client_key = `-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDQNCtaUJ+KPKW6
3PbxpMFP1YOe8Faknnfm38KW3ijae/cKnonG9RXNiR0q5otHb3mDIQHH0lIeJnE+
Img9V+nCKCYbRH0e8HIAbmuFpBtC332emgFocymzWLXZXHEXLXJqplbqavCscx9X
vXqtnYwDNbHtR+G62WgFh56W6LOINZRIS4Z4kbuQd29S0ZiVtF38SUVYc58BDs5z
xRSMQdayiuSpyKrIARFX8f2GvlFqn8n8RFwULxs15xLi3ViH3xaAi6f8xtwEpDOj
UuxaY5B2ZowGgbyDWZhFs1qnwb/5pBuN9GLXM3TJpbXC2UugEmY2AuPBiBpL/lR7
Lk1kx1s3AgEDAoIBAQCKzXI8Nb+xfcPR6KShGIDf460UoDnDFE/vP9cPPsXm/U9c
abEvTg6JBhNx7weE9PuswKvajDa+xEt+wZrTj/EsGsQSLai/Svaq9EeubWeB6lO/
EVZFohvM5c6Q6EtkyPbxxDnxnKBy92o6flHJE7KsznaeL+vR5kVZBRRkmyJazS4s
Z5rbrN9AhSIfyHs9GCQGgsXT6HMsyoJYFastwQ2qj+9L2ypcM8TW+KGzGfJipoJb
l/N/8WHb4ZumA67lfWq4v5JTA5qAUKcfPszEBrUfQ34Tk+73Iiov9f7SXPYxWxVJ
g9PuzfewvJrp6CPv+/mKNt8PmBYkaXlnyjr9tCwLAoGBAPjAVZapQVuIqftcOZtf
Re9fAV9Vvv1FEO8bKJeIsPDlRkdg+TfTMgxhZU0I3P4XdEj7Fa87w4wkA6GkIrOO
W9/usPOYzSdTP5aVEsdGbT8yD2vTST7Aw/GESKTRJA/Fe1PIb5Nz3OijyTusvFE+
XSR3EXb1myX+2rFS0Wbiz2U5AoGBANZFWoeFzREnBcDG60RayjiTg71E1/T4zhvU
e/w+71FNbLZXBrNqgV20F73xOme/Mb13yr+YgXxIEQfFtR6hRxZ8u1jndEzw66Jf
YfHt7EGVceMV2pdP4md5ebebEj7qICfXPxF9IZicwZG3QMR5u0tvnx40iNMWhW0M
rY4FabPvAoGBAKXVjmRw1j0FxqeS0RI/g/TqAOo5Kf4uC0oSGw+wdfXuLtpApiU3
drLrmN4F6Klk+DCnY8on17LCrRZtbHe0PT/0dfe7M2+M1Q8ODITZniohX503hinV
1/ZYMG3gwrUuUjfa9Qz36JsX230d0uDUPhhPYPn5EhlUkcuMi5nsikN7AoGBAI7Y
5wUD3gtvWSsvR4LnMXsNAn4t5U37NBKNp/1/SjYznc7kryJHAOkiun6g0Zp/dn5P
3H+7AP2FYK/ZI2nA2g790jtE+DNLR8GU6/aenYEOS+y5PGTf7ET7pnpnYX9GwBqP
f2D+FmW91mEk1dhRJ4efv2l4WzdkWPNdyQlY8SKfAoGAEIWmowo7EpbR5Boxc2o3
Tl0JbGi1CNJAHtDjEJCd1OxKrMUrK07hOeEKF6y8K/WBuQhFvI2pu16oT4sMal9Z
mEiJdJAFErefPLQGomHLXfq9mDEY13Ug/xAd9aMyYcubIg5XjAqLMrB60HcrLr7Q
2hMCSDdVP2V/F3QVh8DEirE=
-----END PRIVATE KEY-----`
) )



+ 7
- 1
_dev/tstclnt/Dockerfile View File

@@ -21,7 +21,13 @@ ENV USE_64=1 NSS_ENABLE_TLS_1_3=1
# ARG REVISION=e61c0f657100 # ARG REVISION=e61c0f657100


# Draft 22 # Draft 22
ARG REVISION=88c3f3fa581b
#ARG REVISION=88c3f3fa581b

# Draft 23
# ARG REVISION=16c622c9e1cc

# Latest
ARG REVISION=ee357b00f2e6


RUN cd nss && hg pull RUN cd nss && hg pull
RUN cd nss && hg checkout -C $REVISION RUN cd nss && hg checkout -C $REVISION


+ 20
- 0
_dev/utils/fmtcheck.sh View File

@@ -0,0 +1,20 @@
#!/bin/sh
# Copyright 2012 The Go Authors. All rights reserved.
# Use of this source code is governed by a BSD-style
# license that can be found in the LICENSE file.

gofiles=$(find . -iname "*.go" ! -path "*/_dev/*")
#[ -z "$gofiles" ] && exit 0

unformatted=$(gofmt -l $gofiles)
[ -z "$unformatted" ] && exit 0

# Some files are not gofmt'd. Print message and fail.

echo >&2 "Go files must be formatted with gofmt. Please run:"
for fn in $unformatted; do
echo >&2 " gofmt -w $PWD/$fn"
done

exit 1


+ 27
- 0
_dev/utils/pre-commit View File

@@ -0,0 +1,27 @@
#!/bin/sh
# Copyright 2012 The Go Authors. All rights reserved.
# Use of this source code is governed by a BSD-style
# license that can be found in the LICENSE file.

# git gofmt pre-commit hook
#
# To use, store as .git/hooks/pre-commit inside your repository and make sure
# it has execute permissions.
#
# This script does not handle file names that contain spaces.

gofiles=$(git diff --cached --name-only --diff-filter=ACM | grep '\.go$')
[ -z "$gofiles" ] && exit 0

unformatted=$(gofmt -l $gofiles)
[ -z "$unformatted" ] && exit 0

# Some files are not gofmt'd. Print message and fail.

echo >&2 "Go files must be formatted with gofmt. Please run:"
for fn in $unformatted; do
echo >&2 " gofmt -w $PWD/$fn"
done

exit 1


+ 1
- 0
alert.go View File

@@ -38,6 +38,7 @@ const (
alertInappropriateFallback alert = 86 alertInappropriateFallback alert = 86
alertUserCanceled alert = 90 alertUserCanceled alert = 90
alertNoRenegotiation alert = 100 alertNoRenegotiation alert = 100
alertUnsupportedExtension alert = 110
alertCertificateRequired alert = 116 alertCertificateRequired alert = 116
alertNoApplicationProtocol alert = 120 alertNoApplicationProtocol alert = 120
alertSuccess alert = 255 // dummy value returned by unmarshal functions alertSuccess alert = 255 // dummy value returned by unmarshal functions


+ 4
- 2
auth.go View File

@@ -14,9 +14,11 @@ import (
) )


// pickSignatureAlgorithm selects a signature algorithm that is compatible with // pickSignatureAlgorithm selects a signature algorithm that is compatible with
// the given public key and the list of algorithms from the peer and this side.
// the given public key and the list of algorithms from both sides of connection.
// The lists of signature algorithms (peerSigAlgs and ourSigAlgs) are ignored
// for tlsVersion < VersionTLS12.
// //
// The returned SignatureScheme codepoint is only meaningful for TLS 1.2,
// The returned SignatureScheme codepoint is only meaningful for TLS 1.2 and newer
// previous TLS versions have a fixed hash function. // previous TLS versions have a fixed hash function.
func pickSignatureAlgorithm(pubkey crypto.PublicKey, peerSigAlgs, ourSigAlgs []SignatureScheme, tlsVersion uint16) (SignatureScheme, uint8, crypto.Hash, error) { func pickSignatureAlgorithm(pubkey crypto.PublicKey, peerSigAlgs, ourSigAlgs []SignatureScheme, tlsVersion uint16) (SignatureScheme, uint8, crypto.Hash, error) {
if tlsVersion < VersionTLS12 || len(peerSigAlgs) == 0 { if tlsVersion < VersionTLS12 || len(peerSigAlgs) == 0 {


+ 101
- 0
auth_test.go View File

@@ -0,0 +1,101 @@
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package tls

import (
"crypto"
"testing"
)

func TestSignatureSelection(t *testing.T) {
rsaCert := &testRSAPrivateKey.PublicKey
ecdsaCert := &testECDSAPrivateKey.PublicKey
sigsPKCS1WithSHA := []SignatureScheme{PKCS1WithSHA256, PKCS1WithSHA1}
sigsPSSWithSHA := []SignatureScheme{PSSWithSHA256, PSSWithSHA384}
sigsECDSAWithSHA := []SignatureScheme{ECDSAWithP256AndSHA256, ECDSAWithSHA1}

tests := []struct {
pubkey crypto.PublicKey
peerSigAlgs []SignatureScheme
ourSigAlgs []SignatureScheme
tlsVersion uint16

expectedSigAlg SignatureScheme // or 0 if ignored
expectedSigType uint8
expectedHash crypto.Hash
}{
// Hash is fixed for RSA in TLS 1.1 and before.
// https://tools.ietf.org/html/rfc4346#page-44
{rsaCert, nil, nil, VersionTLS11, 0, signaturePKCS1v15, crypto.MD5SHA1},
{rsaCert, nil, nil, VersionTLS10, 0, signaturePKCS1v15, crypto.MD5SHA1},
{rsaCert, nil, nil, VersionSSL30, 0, signaturePKCS1v15, crypto.MD5SHA1},

// Before TLS 1.2, there is no signature_algorithms extension
// nor field in CertificateRequest and digitally-signed and thus
// it should be ignored.
{rsaCert, sigsPKCS1WithSHA, nil, VersionTLS11, 0, signaturePKCS1v15, crypto.MD5SHA1},
{rsaCert, sigsPKCS1WithSHA, sigsPKCS1WithSHA, VersionTLS11, 0, signaturePKCS1v15, crypto.MD5SHA1},
// Use SHA-1 for TLS 1.0 and 1.1 with ECDSA, see https://tools.ietf.org/html/rfc4492#page-20
{ecdsaCert, sigsPKCS1WithSHA, sigsPKCS1WithSHA, VersionTLS11, 0, signatureECDSA, crypto.SHA1},
{ecdsaCert, sigsPKCS1WithSHA, sigsPKCS1WithSHA, VersionTLS10, 0, signatureECDSA, crypto.SHA1},

// TLS 1.2 without signature_algorithms extension
// https://tools.ietf.org/html/rfc5246#page-47
{rsaCert, nil, sigsPKCS1WithSHA, VersionTLS12, PKCS1WithSHA1, signaturePKCS1v15, crypto.SHA1},
{ecdsaCert, nil, sigsPKCS1WithSHA, VersionTLS12, ECDSAWithSHA1, signatureECDSA, crypto.SHA1},

{rsaCert, []SignatureScheme{PKCS1WithSHA1}, sigsPKCS1WithSHA, VersionTLS12, PKCS1WithSHA1, signaturePKCS1v15, crypto.SHA1},
{rsaCert, []SignatureScheme{PKCS1WithSHA256}, sigsPKCS1WithSHA, VersionTLS12, PKCS1WithSHA256, signaturePKCS1v15, crypto.SHA256},
// "sha_hash" may denote hashes other than SHA-1
// https://tools.ietf.org/html/draft-ietf-tls-rfc4492bis-17#page-17
{ecdsaCert, []SignatureScheme{ECDSAWithSHA1}, sigsECDSAWithSHA, VersionTLS12, ECDSAWithSHA1, signatureECDSA, crypto.SHA1},
{ecdsaCert, []SignatureScheme{ECDSAWithP256AndSHA256}, sigsECDSAWithSHA, VersionTLS12, ECDSAWithP256AndSHA256, signatureECDSA, crypto.SHA256},

// RSASSA-PSS is defined in TLS 1.3 for TLS 1.2
// https://tools.ietf.org/html/draft-ietf-tls-tls13-21#page-45
{rsaCert, []SignatureScheme{PSSWithSHA256}, sigsPSSWithSHA, VersionTLS12, PSSWithSHA256, signatureRSAPSS, crypto.SHA256},
}

for testNo, test := range tests {
sigAlg, sigType, hashFunc, err := pickSignatureAlgorithm(test.pubkey, test.peerSigAlgs, test.ourSigAlgs, test.tlsVersion)
if err != nil {
t.Errorf("test[%d]: unexpected error: %v", testNo, err)
}
if test.expectedSigAlg != 0 && test.expectedSigAlg != sigAlg {
t.Errorf("test[%d]: expected signature scheme %#x, got %#x", testNo, test.expectedSigAlg, sigAlg)
}
if test.expectedSigType != sigType {
t.Errorf("test[%d]: expected signature algorithm %#x, got %#x", testNo, test.expectedSigType, sigType)
}
if test.expectedHash != hashFunc {
t.Errorf("test[%d]: expected hash function %#x, got %#x", testNo, test.expectedHash, hashFunc)
}
}

badTests := []struct {
pubkey crypto.PublicKey
peerSigAlgs []SignatureScheme
ourSigAlgs []SignatureScheme
tlsVersion uint16
}{
{rsaCert, sigsECDSAWithSHA, sigsPKCS1WithSHA, VersionTLS12},
{ecdsaCert, sigsPKCS1WithSHA, sigsPKCS1WithSHA, VersionTLS12},
{ecdsaCert, sigsECDSAWithSHA, sigsPKCS1WithSHA, VersionTLS12},
{rsaCert, []SignatureScheme{0}, sigsPKCS1WithSHA, VersionTLS12},

// ECDSA is unspecified for SSL 3.0 in RFC 4492.
// TODO a SSL 3.0 client cannot advertise signature_algorithms,
// but if an application feeds an ECDSA certificate anyway, it
// will be accepted rather than trigger a handshake failure. Ok?
//{ecdsaCert, nil, nil, VersionSSL30},
}

for testNo, test := range badTests {
sigAlg, sigType, hashFunc, err := pickSignatureAlgorithm(test.pubkey, test.peerSigAlgs, test.ourSigAlgs, test.tlsVersion)
if err == nil {
t.Errorf("test[%d]: unexpected success, got %#x %#x %#x", testNo, sigAlg, sigType, hashFunc)
}
}
}

+ 9
- 6
cipher_suites.go View File

@@ -5,6 +5,7 @@
package tls package tls


import ( import (
"crypto"
"crypto/aes" "crypto/aes"
"crypto/cipher" "crypto/cipher"
"crypto/des" "crypto/des"
@@ -12,7 +13,6 @@ import (
"crypto/rc4" "crypto/rc4"
"crypto/sha1" "crypto/sha1"
"crypto/sha256" "crypto/sha256"
"crypto/x509"
"hash" "hash"


"golang_org/x/crypto/chacha20poly1305" "golang_org/x/crypto/chacha20poly1305"
@@ -26,15 +26,15 @@ type keyAgreement interface {
// In the case that the key agreement protocol doesn't use a // In the case that the key agreement protocol doesn't use a
// ServerKeyExchange message, generateServerKeyExchange can return nil, // ServerKeyExchange message, generateServerKeyExchange can return nil,
// nil. // nil.
generateServerKeyExchange(*Config, *Certificate, *clientHelloMsg, *serverHelloMsg) (*serverKeyExchangeMsg, error)
processClientKeyExchange(*Config, *Certificate, *clientKeyExchangeMsg, uint16) ([]byte, error)
generateServerKeyExchange(*Config, crypto.PrivateKey, *clientHelloMsg, *serverHelloMsg) (*serverKeyExchangeMsg, error)
processClientKeyExchange(*Config, crypto.PrivateKey, *clientKeyExchangeMsg, uint16) ([]byte, error)


// On the client side, the next two methods are called in order. // On the client side, the next two methods are called in order.


// This method may not be called if the server doesn't send a // This method may not be called if the server doesn't send a
// ServerKeyExchange message. // ServerKeyExchange message.
processServerKeyExchange(*Config, *clientHelloMsg, *serverHelloMsg, *x509.Certificate, *serverKeyExchangeMsg) error
generateClientKeyExchange(*Config, *clientHelloMsg, *x509.Certificate) ([]byte, *clientKeyExchangeMsg, error)
processServerKeyExchange(*Config, *clientHelloMsg, *serverHelloMsg, crypto.PublicKey, *serverKeyExchangeMsg) error
generateClientKeyExchange(*Config, *clientHelloMsg, crypto.PublicKey) ([]byte, *clientKeyExchangeMsg, error)
} }


const ( const (
@@ -174,7 +174,10 @@ type fixedNonceAEAD struct {
aead cipher.AEAD aead cipher.AEAD
} }


func (f *fixedNonceAEAD) NonceSize() int { return 8 }
func (f *fixedNonceAEAD) NonceSize() int { return 8 }

// Overhead returns the maximum difference between the lengths of a
// plaintext and its ciphertext.
func (f *fixedNonceAEAD) Overhead() int { return f.aead.Overhead() } func (f *fixedNonceAEAD) Overhead() int { return f.aead.Overhead() }
func (f *fixedNonceAEAD) explicitNonceLen() int { return 8 } func (f *fixedNonceAEAD) explicitNonceLen() int { return 8 }




+ 97
- 41
common.go View File

@@ -7,12 +7,12 @@ package tls
import ( import (
"container/list" "container/list"
"crypto" "crypto"
"crypto/internal/cipherhw"
"crypto/rand" "crypto/rand"
"crypto/sha512" "crypto/sha512"
"crypto/x509" "crypto/x509"
"errors" "errors"
"fmt" "fmt"
"internal/cpu"
"io" "io"
"math/big" "math/big"
"net" "net"
@@ -22,14 +22,11 @@ import (
) )


const ( const (
VersionSSL30 = 0x0300
VersionTLS10 = 0x0301
VersionTLS11 = 0x0302
VersionTLS12 = 0x0303
VersionTLS13 = 0x0304
VersionTLS13Draft18 = 0x7f00 | 18
VersionTLS13Draft21 = 0x7f00 | 21
VersionTLS13Draft22 = 0x7f00 | 22
VersionSSL30 = 0x0300
VersionTLS10 = 0x0301
VersionTLS11 = 0x0302
VersionTLS12 = 0x0303
VersionTLS13 = 0x0304
) )


const ( const (
@@ -40,7 +37,7 @@ const (
maxWarnAlertCount = 5 // maximum number of consecutive warning alerts maxWarnAlertCount = 5 // maximum number of consecutive warning alerts


minVersion = VersionTLS12 minVersion = VersionTLS12
maxVersion = VersionTLS13Draft22
maxVersion = VersionTLS13
) )


// TLS record types. // TLS record types.
@@ -79,22 +76,25 @@ const (


// TLS extension numbers // TLS extension numbers
const ( const (
extensionServerName uint16 = 0
extensionStatusRequest uint16 = 5
extensionSupportedCurves uint16 = 10 // Supported Groups in 1.3 nomenclature
extensionSupportedPoints uint16 = 11
extensionSignatureAlgorithms uint16 = 13
extensionALPN uint16 = 16
extensionSCT uint16 = 18 // https://tools.ietf.org/html/rfc6962#section-6
extensionSessionTicket uint16 = 35
extensionKeyShare uint16 = 40
extensionPreSharedKey uint16 = 41
extensionEarlyData uint16 = 42
extensionSupportedVersions uint16 = 43
extensionPSKKeyExchangeModes uint16 = 45
extensionCAs uint16 = 47
extensionNextProtoNeg uint16 = 13172 // not IANA assigned
extensionRenegotiationInfo uint16 = 0xff01
extensionServerName uint16 = 0
extensionStatusRequest uint16 = 5
extensionSupportedCurves uint16 = 10 // Supported Groups in 1.3 nomenclature
extensionSupportedPoints uint16 = 11
extensionSignatureAlgorithms uint16 = 13
extensionALPN uint16 = 16
extensionSCT uint16 = 18 // https://tools.ietf.org/html/rfc6962#section-6
extensionEMS uint16 = 23
extensionSessionTicket uint16 = 35
extensionPreSharedKey uint16 = 41
extensionEarlyData uint16 = 42
extensionSupportedVersions uint16 = 43
extensionPSKKeyExchangeModes uint16 = 45
extensionCAs uint16 = 47
extensionSignatureAlgorithmsCert uint16 = 50
extensionKeyShare uint16 = 51
extensionNextProtoNeg uint16 = 13172 // not IANA assigned
extensionRenegotiationInfo uint16 = 0xff01
extensionDelegatedCredential uint16 = 0xff02 // TODO(any) Get IANA assignment
) )


// TLS signaling cipher suite values // TLS signaling cipher suite values
@@ -116,10 +116,15 @@ const (
type CurveID uint16 type CurveID uint16


const ( const (
// Exported IDs
CurveP256 CurveID = 23 CurveP256 CurveID = 23
CurveP384 CurveID = 24 CurveP384 CurveID = 24
CurveP521 CurveID = 25 CurveP521 CurveID = 25
X25519 CurveID = 29 X25519 CurveID = 29

// Experimental KEX
HybridSIDHp503Curve25519 CurveID = 0xFE30
HybridSIKEp503Curve25519 CurveID = 0xFE32
) )


// TLS 1.3 Key Share // TLS 1.3 Key Share
@@ -164,9 +169,10 @@ const (
// Rest of these are reserved by the TLS spec // Rest of these are reserved by the TLS spec
) )


// Signature algorithms for TLS 1.2 (See RFC 5246, section A.4.1)
// Signature algorithms (for internal signaling use). Starting at 16 to avoid overlap with
// TLS 1.2 codepoints (RFC 5246, section A.4.1), with which these have nothing to do.
const ( const (
signaturePKCS1v15 uint8 = iota + 1
signaturePKCS1v15 uint8 = iota + 16
signatureECDSA signatureECDSA
signatureRSAPSS signatureRSAPSS
) )
@@ -216,6 +222,7 @@ type ConnectionState struct {
VerifiedChains [][]*x509.Certificate // verified chains built from PeerCertificates VerifiedChains [][]*x509.Certificate // verified chains built from PeerCertificates
SignedCertificateTimestamps [][]byte // SCTs from the server, if any SignedCertificateTimestamps [][]byte // SCTs from the server, if any
OCSPResponse []byte // stapled OCSP response from server, if any OCSPResponse []byte // stapled OCSP response from server, if any
DelegatedCredential []byte // Delegated credential sent by the server, if any


// TLSUnique contains the "tls-unique" channel binding value (see RFC // TLSUnique contains the "tls-unique" channel binding value (see RFC
// 5929, section 3). For resumed sessions this value will be nil // 5929, section 3). For resumed sessions this value will be nil
@@ -258,6 +265,7 @@ type ClientSessionState struct {
masterSecret []byte // MasterSecret generated by client on a full handshake masterSecret []byte // MasterSecret generated by client on a full handshake
serverCertificates []*x509.Certificate // Certificate chain presented by the server serverCertificates []*x509.Certificate // Certificate chain presented by the server
verifiedChains [][]*x509.Certificate // Certificate chains we built for verification verifiedChains [][]*x509.Certificate // Certificate chains we built for verification
useEMS bool // State of extended master secret
} }


// ClientSessionCache is a cache of ClientSessionState objects that can be used // ClientSessionCache is a cache of ClientSessionState objects that can be used
@@ -354,6 +362,10 @@ type ClientHelloInfo struct {
// immediately available for Read. // immediately available for Read.
Offered0RTTData bool Offered0RTTData bool


// AcceptsDelegatedCredential is true if the client indicated willingness
// to negotiate the delegated credential extension.
AcceptsDelegatedCredential bool

// The Fingerprint is an sequence of bytes unique to this Client Hello. // The Fingerprint is an sequence of bytes unique to this Client Hello.
// It can be used to prevent or mitigate 0-RTT data replays as it's // It can be used to prevent or mitigate 0-RTT data replays as it's
// guaranteed that a replayed connection will have the same Fingerprint. // guaranteed that a replayed connection will have the same Fingerprint.
@@ -532,7 +544,8 @@ type Config struct {
PreferServerCipherSuites bool PreferServerCipherSuites bool


// SessionTicketsDisabled may be set to true to disable session ticket // SessionTicketsDisabled may be set to true to disable session ticket
// (resumption) support.
// (resumption) support. Note that on clients, session ticket support is
// also disabled if ClientSessionCache is nil.
SessionTicketsDisabled bool SessionTicketsDisabled bool


// SessionTicketKey is used by TLS servers to provide session // SessionTicketKey is used by TLS servers to provide session
@@ -546,7 +559,7 @@ type Config struct {
SessionTicketKey [32]byte SessionTicketKey [32]byte


// ClientSessionCache is a cache of ClientSessionState entries for TLS // ClientSessionCache is a cache of ClientSessionState entries for TLS
// session resumption.
// session resumption. It is only used by clients.
ClientSessionCache ClientSessionCache ClientSessionCache ClientSessionCache


// MinVersion contains the minimum SSL/TLS version that is acceptable. // MinVersion contains the minimum SSL/TLS version that is acceptable.
@@ -607,6 +620,26 @@ type Config struct {
// session tickets, instead of SessionTicketKey. // session tickets, instead of SessionTicketKey.
SessionTicketSealer SessionTicketSealer SessionTicketSealer SessionTicketSealer


// AcceptDelegatedCredential is true if the client is willing to negotiate
// the delegated credential extension.
//
// This value has no meaning for the server.
//
// See https://tools.ietf.org/html/draft-ietf-tls-subcerts-02.
AcceptDelegatedCredential bool

// GetDelegatedCredential returns a DC and its private key for use in the
// delegated credential extension. The inputs to the callback are some
// information parsed from the ClientHello, as well as the protocol version
// selected by the server. This is necessary because the DC is bound to the
// protocol version in which it's used. The return value is the raw DC
// encoded in the wire format specified in
// https://tools.ietf.org/html/draft-ietf-tls-subcerts-02. If the return
// value is nil, then the server will not offer negotiate the extension.
//
// This value has no meaning for the client.
GetDelegatedCredential func(*ClientHelloInfo, uint16) ([]byte, crypto.PrivateKey, error)

serverInitOnce sync.Once // guards calling (*Config).serverInit serverInitOnce sync.Once // guards calling (*Config).serverInit


// mutex protects sessionTicketKeys. // mutex protects sessionTicketKeys.
@@ -616,6 +649,10 @@ type Config struct {
// for new tickets and any subsequent keys can be used to decrypt old // for new tickets and any subsequent keys can be used to decrypt old
// tickets. // tickets.
sessionTicketKeys []ticketKey sessionTicketKeys []ticketKey

// UseExtendedMasterSecret indicates whether or not the connection
// should use the extended master secret computation if available
UseExtendedMasterSecret bool
} }


// ticketKeyNameLen is the number of bytes of identifier that is prepended to // ticketKeyNameLen is the number of bytes of identifier that is prepended to
@@ -683,7 +720,10 @@ func (c *Config) Clone() *Config {
Accept0RTTData: c.Accept0RTTData, Accept0RTTData: c.Accept0RTTData,
Max0RTTDataSize: c.Max0RTTDataSize, Max0RTTDataSize: c.Max0RTTDataSize,
SessionTicketSealer: c.SessionTicketSealer, SessionTicketSealer: c.SessionTicketSealer,
AcceptDelegatedCredential: c.AcceptDelegatedCredential,
GetDelegatedCredential: c.GetDelegatedCredential,
sessionTicketKeys: sessionTicketKeys, sessionTicketKeys: sessionTicketKeys,
UseExtendedMasterSecret: c.UseExtendedMasterSecret,
} }
} }


@@ -854,12 +894,6 @@ func (c *Config) pickVersion(peerSupportedVersions []uint16) (uint16, bool) {
// configSuppVersArray is the backing array of Config.getSupportedVersions // configSuppVersArray is the backing array of Config.getSupportedVersions
var configSuppVersArray = [...]uint16{VersionTLS13, VersionTLS12, VersionTLS11, VersionTLS10, VersionSSL30} var configSuppVersArray = [...]uint16{VersionTLS13, VersionTLS12, VersionTLS11, VersionTLS10, VersionSSL30}


// tls13DraftSuppVersArray is the backing array of Config.getSupportedVersions
// with TLS 1.3 draft versions included.
//
// TODO: remove once TLS 1.3 is finalised.
var tls13DraftSuppVersArray = [...]uint16{VersionTLS13Draft22, VersionTLS12, VersionTLS11, VersionTLS10, VersionSSL30}

// getSupportedVersions returns the protocol versions that are supported by the // getSupportedVersions returns the protocol versions that are supported by the
// current configuration. // current configuration.
func (c *Config) getSupportedVersions() []uint16 { func (c *Config) getSupportedVersions() []uint16 {
@@ -875,10 +909,6 @@ func (c *Config) getSupportedVersions() []uint16 {
if maxVersion < minVersion { if maxVersion < minVersion {
return nil return nil
} }
// TODO: remove once TLS 1.3 is finalised.
if maxVersion == VersionTLS13 {
return tls13DraftSuppVersArray[:len(tls13DraftSuppVersArray)-int(minVersion-VersionSSL30)]
}
return configSuppVersArray[VersionTLS13-maxVersion : VersionTLS13-minVersion+1] return configSuppVersArray[VersionTLS13-maxVersion : VersionTLS13-minVersion+1]
} }


@@ -1095,7 +1125,19 @@ func defaultTLS13CipherSuites() []uint16 {


func initDefaultCipherSuites() { func initDefaultCipherSuites() {
var topCipherSuites, topTLS13CipherSuites []uint16 var topCipherSuites, topTLS13CipherSuites []uint16
if cipherhw.AESGCMSupport() {

// Check the cpu flags for each platform that has optimized GCM implementations.
// Worst case, these variables will just all be false
hasGCMAsmAMD64 := cpu.X86.HasAES && cpu.X86.HasPCLMULQDQ

hasGCMAsmARM64 := cpu.ARM64.HasAES && cpu.ARM64.HasPMULL

// Keep in sync with crypto/aes/cipher_s390x.go.
hasGCMAsmS390X := cpu.S390X.HasAES && cpu.S390X.HasAESCBC && cpu.S390X.HasAESCTR && (cpu.S390X.HasGHASH || cpu.S390X.HasAESGCM)

hasGCMAsm := hasGCMAsmAMD64 || hasGCMAsmARM64 || hasGCMAsmS390X

if hasGCMAsm {
// If AES-GCM hardware is provided then prioritise AES-GCM // If AES-GCM hardware is provided then prioritise AES-GCM
// cipher suites. // cipher suites.
topTLS13CipherSuites = []uint16{ topTLS13CipherSuites = []uint16{
@@ -1185,3 +1227,17 @@ func signatureFromSignatureScheme(signatureAlgorithm SignatureScheme) uint8 {
return 0 return 0
} }
} }

// TODO(kk): Use variable length encoding?
func getUint24(b []byte) int {
n := int(b[2])
n += int(b[1] << 8)
n += int(b[0] << 16)
return n
}

func putUint24(b []byte, n int) {
b[0] = byte(n >> 16)
b[1] = byte(n >> 8)
b[2] = byte(n & 0xff)
}

+ 40
- 16
conn.go View File

@@ -11,6 +11,7 @@ import (
"crypto/cipher" "crypto/cipher"
"crypto/subtle" "crypto/subtle"
"crypto/x509" "crypto/x509"
"encoding/binary"
"errors" "errors"
"fmt" "fmt"
"io" "io"
@@ -51,17 +52,22 @@ type Conn struct {
didResume bool // whether this connection was a session resumption didResume bool // whether this connection was a session resumption
cipherSuite uint16 cipherSuite uint16
ocspResponse []byte // stapled OCSP response ocspResponse []byte // stapled OCSP response
scts [][]byte // signed certificate timestamps from server
scts [][]byte // Signed certificate timestamps from server
peerCertificates []*x509.Certificate peerCertificates []*x509.Certificate
// verifiedChains contains the certificate chains that we built, as // verifiedChains contains the certificate chains that we built, as
// opposed to the ones presented by the server. // opposed to the ones presented by the server.
verifiedChains [][]*x509.Certificate verifiedChains [][]*x509.Certificate
// verifiedDc is set by a client who negotiates the use of a valid delegated
// credential.
verifiedDc *delegatedCredential
// serverName contains the server name indicated by the client, if any. // serverName contains the server name indicated by the client, if any.
serverName string serverName string
// secureRenegotiation is true if the server echoed the secure // secureRenegotiation is true if the server echoed the secure
// renegotiation extension. (This is meaningless as a server because // renegotiation extension. (This is meaningless as a server because
// renegotiation is not supported in that case.) // renegotiation is not supported in that case.)
secureRenegotiation bool secureRenegotiation bool
// indicates wether extended MasterSecret extension is used (see RFC7627)
useEMS bool


// clientFinishedIsFirst is true if the client sent the first Finished // clientFinishedIsFirst is true if the client sent the first Finished
// message during the most recent handshake. This is recorded because // message during the most recent handshake. This is recorded because
@@ -355,6 +361,15 @@ func (hc *halfConn) decrypt(b *block) (ok bool, prefixLen int, alertValue alert)
hc.additionalData[11] = byte(n >> 8) hc.additionalData[11] = byte(n >> 8)
hc.additionalData[12] = byte(n) hc.additionalData[12] = byte(n)
additionalData = hc.additionalData[:] additionalData = hc.additionalData[:]
} else {
if len(payload) > int((1<<14)+256) {
return false, 0, alertRecordOverflow
}
// Check AD header, see 5.2 of RFC8446
additionalData = make([]byte, 5)
additionalData[0] = byte(recordTypeApplicationData)
binary.BigEndian.PutUint16(additionalData[1:], VersionTLS12)
binary.BigEndian.PutUint16(additionalData[3:], uint16(len(payload)))
} }
var err error var err error
payload, err = c.Open(payload[:0], nonce, payload, additionalData) payload, err = c.Open(payload[:0], nonce, payload, additionalData)
@@ -457,36 +472,42 @@ func (hc *halfConn) encrypt(b *block, explicitIVLen int) (bool, alert) {
case cipher.Stream: case cipher.Stream:
c.XORKeyStream(payload, payload) c.XORKeyStream(payload, payload)
case aead: case aead:
// explicitIVLen is always 0 for TLS1.3
payloadLen := len(b.data) - recordHeaderLen - explicitIVLen payloadLen := len(b.data) - recordHeaderLen - explicitIVLen
overhead := c.Overhead()
if hc.version >= VersionTLS13 {
overhead++
}
b.resize(len(b.data) + overhead)

payloadOffset := recordHeaderLen + explicitIVLen
nonce := b.data[recordHeaderLen : recordHeaderLen+explicitIVLen] nonce := b.data[recordHeaderLen : recordHeaderLen+explicitIVLen]
if len(nonce) == 0 { if len(nonce) == 0 {
nonce = hc.seq[:] nonce = hc.seq[:]
} }
payload = b.data[recordHeaderLen+explicitIVLen:]
payload = payload[:payloadLen]


var additionalData []byte var additionalData []byte
if hc.version < VersionTLS13 { if hc.version < VersionTLS13 {
// make room in a buffer for payload + MAC
b.resize(len(b.data) + c.Overhead())

payload = b.data[payloadOffset : payloadOffset+payloadLen]
copy(hc.additionalData[:], hc.seq[:]) copy(hc.additionalData[:], hc.seq[:])
copy(hc.additionalData[8:], b.data[:3]) copy(hc.additionalData[8:], b.data[:3])
hc.additionalData[11] = byte(payloadLen >> 8)
hc.additionalData[12] = byte(payloadLen)
binary.BigEndian.PutUint16(hc.additionalData[11:], uint16(payloadLen))
additionalData = hc.additionalData[:] additionalData = hc.additionalData[:]
}
} else {
// make room in a buffer for TLSCiphertext.encrypted_record:
// payload + MAC + extra data if needed
b.resize(len(b.data) + c.Overhead() + 1)


if hc.version >= VersionTLS13 {
// opaque type
payload = payload[:len(payload)+1]
payload = b.data[payloadOffset : payloadOffset+payloadLen+1]
// 1 byte of content type is appended to payload and encrypted
payload[len(payload)-1] = b.data[0] payload[len(payload)-1] = b.data[0]

// opaque_type
b.data[0] = byte(recordTypeApplicationData) b.data[0] = byte(recordTypeApplicationData)
}


// Add AD header, see 5.2 of RFC8446
additionalData = make([]byte, 5)
additionalData[0] = b.data[0]
binary.BigEndian.PutUint16(additionalData[1:], VersionTLS12)
binary.BigEndian.PutUint16(additionalData[3:], uint16(len(payload)+c.Overhead()))
}
c.Seal(payload[:0], nonce, payload, additionalData) c.Seal(payload[:0], nonce, payload, additionalData)
case cbcMode: case cbcMode:
blockSize := c.BlockSize() blockSize := c.BlockSize()
@@ -1663,6 +1684,9 @@ func (c *Conn) ConnectionState() ConnectionState {
state.VerifiedChains = c.verifiedChains state.VerifiedChains = c.verifiedChains
state.SignedCertificateTimestamps = c.scts state.SignedCertificateTimestamps = c.scts
state.OCSPResponse = c.ocspResponse state.OCSPResponse = c.ocspResponse
if c.verifiedDc != nil {
state.DelegatedCredential = c.verifiedDc.raw
}
state.HandshakeConfirmed = atomic.LoadInt32(&c.handshakeConfirmed) == 1 state.HandshakeConfirmed = atomic.LoadInt32(&c.handshakeConfirmed) == 1
if !state.HandshakeConfirmed { if !state.HandshakeConfirmed {
state.Unique0RTTToken = c.binder state.Unique0RTTToken = c.binder


+ 77
- 4
example_test.go View File

@@ -11,6 +11,7 @@ import (
"net/http" "net/http"
"net/http/httptest" "net/http/httptest"
"os" "os"
"time"
) )


// zeroSource is an io.Reader that returns an unlimited number of zero bytes. // zeroSource is an io.Reader that returns an unlimited number of zero bytes.
@@ -155,8 +156,80 @@ func ExampleConfig_keyLogWriter_TLS13() {
// preferences. // preferences.


// Output: // Output:
// CLIENT_HANDSHAKE_TRAFFIC_SECRET 0000000000000000000000000000000000000000000000000000000000000000 ab02b68658d18ef1a4056b3094fe511b43084d40e9a6518753a7f832da724292
// SERVER_HANDSHAKE_TRAFFIC_SECRET 0000000000000000000000000000000000000000000000000000000000000000 d2e96648d170e2524bee07b651f4cca932a52247493ca33cc0714260a7424b2d
// SERVER_TRAFFIC_SECRET_0 0000000000000000000000000000000000000000000000000000000000000000 371fab23269e3cd73496e0e78f3dbc487f7cd5a563cc9f8c1a71be242268c375
// CLIENT_TRAFFIC_SECRET_0 0000000000000000000000000000000000000000000000000000000000000000 ca30484e48ec9a6f3b05b41c7492dbed8dea8e92d2abece2824a96052ac8ed8d
// CLIENT_HANDSHAKE_TRAFFIC_SECRET 0000000000000000000000000000000000000000000000000000000000000000 b946c84f46f53bd410368a1fd7d53873e74bedd53b4b1a4b125be40c8b0510a1
// SERVER_HANDSHAKE_TRAFFIC_SECRET 0000000000000000000000000000000000000000000000000000000000000000 b6c44e95e34cb2616ff2e9a1163577aa1aa5cb3af8df16d0fdbbbaf15f415c8e
// SERVER_TRAFFIC_SECRET_0 0000000000000000000000000000000000000000000000000000000000000000 cbecc42509a124ae517f6c9aaae1961d755ab4268548b40b0c7840a9643240e8
// CLIENT_TRAFFIC_SECRET_0 0000000000000000000000000000000000000000000000000000000000000000 8f6dd1476706ea8147d829347937694496a7d62d6d01de0a1b4820140d01cad0
}

func ExampleLoadX509KeyPair() {
cert, err := tls.LoadX509KeyPair("testdata/example-cert.pem", "testdata/example-key.pem")
if err != nil {
log.Fatal(err)
}
cfg := &tls.Config{Certificates: []tls.Certificate{cert}}
listener, err := tls.Listen("tcp", ":2000", cfg)
if err != nil {
log.Fatal(err)
}
_ = listener
}

func ExampleX509KeyPair() {
certPem := []byte(`-----BEGIN CERTIFICATE-----
MIIBhTCCASugAwIBAgIQIRi6zePL6mKjOipn+dNuaTAKBggqhkjOPQQDAjASMRAw
DgYDVQQKEwdBY21lIENvMB4XDTE3MTAyMDE5NDMwNloXDTE4MTAyMDE5NDMwNlow
EjEQMA4GA1UEChMHQWNtZSBDbzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABD0d
7VNhbWvZLWPuj/RtHFjvtJBEwOkhbN/BnnE8rnZR8+sbwnc/KhCk3FhnpHZnQz7B
5aETbbIgmuvewdjvSBSjYzBhMA4GA1UdDwEB/wQEAwICpDATBgNVHSUEDDAKBggr
BgEFBQcDATAPBgNVHRMBAf8EBTADAQH/MCkGA1UdEQQiMCCCDmxvY2FsaG9zdDo1
NDUzgg4xMjcuMC4wLjE6NTQ1MzAKBggqhkjOPQQDAgNIADBFAiEA2zpJEPQyz6/l
Wf86aX6PepsntZv2GYlA5UpabfT2EZICICpJ5h/iI+i341gBmLiAFQOyTDT+/wQc
6MF9+Yw1Yy0t
-----END CERTIFICATE-----`)
keyPem := []byte(`-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIIrYSSNQFaA2Hwf1duRSxKtLYX5CB04fSeQ6tF1aY/PuoAoGCCqGSM49
AwEHoUQDQgAEPR3tU2Fta9ktY+6P9G0cWO+0kETA6SFs38GecTyudlHz6xvCdz8q
EKTcWGekdmdDPsHloRNtsiCa697B2O9IFA==
-----END EC PRIVATE KEY-----`)
cert, err := tls.X509KeyPair(certPem, keyPem)
if err != nil {
log.Fatal(err)
}
cfg := &tls.Config{Certificates: []tls.Certificate{cert}}
listener, err := tls.Listen("tcp", ":2000", cfg)
if err != nil {
log.Fatal(err)
}
_ = listener
}

func ExampleX509KeyPair_httpServer() {
certPem := []byte(`-----BEGIN CERTIFICATE-----
MIIBhTCCASugAwIBAgIQIRi6zePL6mKjOipn+dNuaTAKBggqhkjOPQQDAjASMRAw
DgYDVQQKEwdBY21lIENvMB4XDTE3MTAyMDE5NDMwNloXDTE4MTAyMDE5NDMwNlow
EjEQMA4GA1UEChMHQWNtZSBDbzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABD0d
7VNhbWvZLWPuj/RtHFjvtJBEwOkhbN/BnnE8rnZR8+sbwnc/KhCk3FhnpHZnQz7B
5aETbbIgmuvewdjvSBSjYzBhMA4GA1UdDwEB/wQEAwICpDATBgNVHSUEDDAKBggr
BgEFBQcDATAPBgNVHRMBAf8EBTADAQH/MCkGA1UdEQQiMCCCDmxvY2FsaG9zdDo1
NDUzgg4xMjcuMC4wLjE6NTQ1MzAKBggqhkjOPQQDAgNIADBFAiEA2zpJEPQyz6/l
Wf86aX6PepsntZv2GYlA5UpabfT2EZICICpJ5h/iI+i341gBmLiAFQOyTDT+/wQc
6MF9+Yw1Yy0t
-----END CERTIFICATE-----`)
keyPem := []byte(`-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIIrYSSNQFaA2Hwf1duRSxKtLYX5CB04fSeQ6tF1aY/PuoAoGCCqGSM49
AwEHoUQDQgAEPR3tU2Fta9ktY+6P9G0cWO+0kETA6SFs38GecTyudlHz6xvCdz8q
EKTcWGekdmdDPsHloRNtsiCa697B2O9IFA==
-----END EC PRIVATE KEY-----`)
cert, err := tls.X509KeyPair(certPem, keyPem)
if err != nil {
log.Fatal(err)
}
cfg := &tls.Config{Certificates: []tls.Certificate{cert}}
srv := &http.Server{
TLSConfig: cfg,
ReadTimeout: time.Minute,
WriteTimeout: time.Minute,
}
log.Fatal(srv.ListenAndServeTLS("", ""))
} }

+ 14
- 6
generate_cert.go View File

@@ -146,16 +146,24 @@ func main() {
if err != nil { if err != nil {
log.Fatalf("failed to open cert.pem for writing: %s", err) log.Fatalf("failed to open cert.pem for writing: %s", err)
} }
pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
certOut.Close()
log.Print("written cert.pem\n")
if err := pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes}); err != nil {
log.Fatalf("failed to write data to cert.pem: %s", err)
}
if err := certOut.Close(); err != nil {
log.Fatalf("error closing cert.pem: %s", err)
}
log.Print("wrote cert.pem\n")


keyOut, err := os.OpenFile("key.pem", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600) keyOut, err := os.OpenFile("key.pem", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
if err != nil { if err != nil {
log.Print("failed to open key.pem for writing:", err) log.Print("failed to open key.pem for writing:", err)
return return
} }
pem.Encode(keyOut, pemBlockForKey(priv))
keyOut.Close()
log.Print("written key.pem\n")
if err := pem.Encode(keyOut, pemBlockForKey(priv)); err != nil {
log.Fatalf("failed to write data to key.pem: %s", err)
}
if err := keyOut.Close(); err != nil {
log.Fatalf("error closing key.pem: %s", err)
}
log.Print("wrote key.pem\n")
} }

+ 76
- 9
handshake_client.go View File

@@ -65,7 +65,9 @@ func makeClientHello(config *Config) (*clientHelloMsg, error) {
supportedPoints: []uint8{pointFormatUncompressed}, supportedPoints: []uint8{pointFormatUncompressed},
nextProtoNeg: len(config.NextProtos) > 0, nextProtoNeg: len(config.NextProtos) > 0,
secureRenegotiationSupported: true, secureRenegotiationSupported: true,
delegatedCredential: config.AcceptDelegatedCredential,
alpnProtocols: config.NextProtos, alpnProtocols: config.NextProtos,
extendedMSSupported: config.UseExtendedMasterSecret,
} }
possibleCipherSuites := config.cipherSuites() possibleCipherSuites := config.cipherSuites()
hello.cipherSuites = make([]uint16, 0, len(possibleCipherSuites)) hello.cipherSuites = make([]uint16, 0, len(possibleCipherSuites))
@@ -106,6 +108,7 @@ NextCipherSuite:
hello.vers = VersionTLS12 hello.vers = VersionTLS12
hello.supportedVersions = config.getSupportedVersions() hello.supportedVersions = config.getSupportedVersions()
hello.supportedSignatureAlgorithms = supportedSignatureAlgorithms13 hello.supportedSignatureAlgorithms = supportedSignatureAlgorithms13
hello.supportedSignatureAlgorithmsCert = supportedSigAlgorithmsCert(supportedSignatureAlgorithms13)
} }


return hello, nil return hello, nil
@@ -191,7 +194,7 @@ func (c *Conn) clientHandshake() error {
// Create one keyshare for the first default curve. If it is not // Create one keyshare for the first default curve. If it is not
// appropriate, the server should raise a HRR. // appropriate, the server should raise a HRR.
defaultGroup := c.config.curvePreferences()[0] defaultGroup := c.config.curvePreferences()[0]
hs.privateKey, clientKS, err = c.config.generateKeyShare(defaultGroup)
hs.privateKey, clientKS, err = c.generateKeyShare(defaultGroup)
if err != nil { if err != nil {
c.sendAlert(alertInternalError) c.sendAlert(alertInternalError)
return err return err
@@ -411,6 +414,50 @@ func (hs *clientHandshakeState) processCertsFromServer(certificates [][]byte) er
return nil return nil
} }


// processDelegatedCredentialFromServer unmarshals the delegated credential
// offered by the server (if present) and validates it using the peer
// certificate and the signature scheme (`scheme`) indicated by the server in
// the "signature_scheme" extension.
func (hs *clientHandshakeState) processDelegatedCredentialFromServer(serialized []byte, scheme SignatureScheme) error {
c := hs.c

var dc *delegatedCredential
var err error
if serialized != nil {
// Assert that the DC extension was indicated by the client.
if !hs.hello.delegatedCredential {
c.sendAlert(alertUnexpectedMessage)
return errors.New("tls: got delegated credential extension without indication")
}

// Parse the delegated credential.
dc, err = unmarshalDelegatedCredential(serialized)
if err != nil {
c.sendAlert(alertDecodeError)
return fmt.Errorf("tls: delegated credential: %s", err)
}
}

if dc != nil && !c.config.InsecureSkipVerify {
if v, err := dc.validate(c.peerCertificates[0], c.config.time()); err != nil {
c.sendAlert(alertIllegalParameter)
return fmt.Errorf("delegated credential: %s", err)
} else if !v {
c.sendAlert(alertIllegalParameter)
return errors.New("delegated credential: signature invalid")
} else if dc.cred.expectedVersion != hs.c.vers {
c.sendAlert(alertIllegalParameter)
return errors.New("delegated credential: protocol version mismatch")
} else if dc.cred.expectedCertVerifyAlgorithm != scheme {
c.sendAlert(alertIllegalParameter)
return errors.New("delegated credential: signature scheme mismatch")
}
}

c.verifiedDc = dc
return nil
}

func (hs *clientHandshakeState) doFullHandshake() error { func (hs *clientHandshakeState) doFullHandshake() error {
c := hs.c c := hs.c


@@ -476,10 +523,14 @@ func (hs *clientHandshakeState) doFullHandshake() error {


keyAgreement := hs.suite.ka(c.vers) keyAgreement := hs.suite.ka(c.vers)


// Set the public key used to verify the handshake.
pk := c.peerCertificates[0].PublicKey

skx, ok := msg.(*serverKeyExchangeMsg) skx, ok := msg.(*serverKeyExchangeMsg)
if ok { if ok {
hs.finishedHash.Write(skx.marshal()) hs.finishedHash.Write(skx.marshal())
err = keyAgreement.processServerKeyExchange(c.config, hs.hello, hs.serverHello, c.peerCertificates[0], skx)

err = keyAgreement.processServerKeyExchange(c.config, hs.hello, hs.serverHello, pk, skx)
if err != nil { if err != nil {
c.sendAlert(alertUnexpectedMessage) c.sendAlert(alertUnexpectedMessage)
return err return err
@@ -528,7 +579,7 @@ func (hs *clientHandshakeState) doFullHandshake() error {
} }
} }


preMasterSecret, ckx, err := keyAgreement.generateClientKeyExchange(c.config, hs.hello, c.peerCertificates[0])
preMasterSecret, ckx, err := keyAgreement.generateClientKeyExchange(c.config, hs.hello, pk)
if err != nil { if err != nil {
c.sendAlert(alertInternalError) c.sendAlert(alertInternalError)
return err return err
@@ -539,6 +590,13 @@ func (hs *clientHandshakeState) doFullHandshake() error {
return err return err
} }
} }
c.useEMS = hs.serverHello.extendedMSSupported
hs.masterSecret = masterFromPreMasterSecret(c.vers, hs.suite, preMasterSecret, hs.hello.random, hs.serverHello.random, hs.finishedHash, c.useEMS)

if err := c.config.writeKeyLog("CLIENT_RANDOM", hs.hello.random, hs.masterSecret); err != nil {
c.sendAlert(alertInternalError)
return errors.New("tls: failed to write to key log: " + err.Error())
}


if chainToSend != nil && len(chainToSend.Certificate) > 0 { if chainToSend != nil && len(chainToSend.Certificate) > 0 {
certVerify := &certificateVerifyMsg{ certVerify := &certificateVerifyMsg{
@@ -581,12 +639,6 @@ func (hs *clientHandshakeState) doFullHandshake() error {
} }
} }


hs.masterSecret = masterFromPreMasterSecret(c.vers, hs.suite, preMasterSecret, hs.hello.random, hs.serverHello.random)
if err := c.config.writeKeyLog("CLIENT_RANDOM", hs.hello.random, hs.masterSecret); err != nil {
c.sendAlert(alertInternalError)
return errors.New("tls: failed to write to key log: " + err.Error())
}

hs.finishedHash.discardHandshakeBuffer() hs.finishedHash.discardHandshakeBuffer()


return nil return nil
@@ -647,6 +699,16 @@ func (hs *clientHandshakeState) processServerHello() (bool, error) {
} }
} }


if hs.serverHello.extendedMSSupported {
if hs.hello.extendedMSSupported {
c.useEMS = true
} else {
// server wants to calculate master secret in a different way than client
c.sendAlert(alertUnsupportedExtension)
return false, errors.New("tls: unexpected extension (EMS) received in SH")
}
}

clientDidNPN := hs.hello.nextProtoNeg clientDidNPN := hs.hello.nextProtoNeg
clientDidALPN := len(hs.hello.alpnProtocols) > 0 clientDidALPN := len(hs.hello.alpnProtocols) > 0
serverHasNPN := hs.serverHello.nextProtoNeg serverHasNPN := hs.serverHello.nextProtoNeg
@@ -677,6 +739,10 @@ func (hs *clientHandshakeState) processServerHello() (bool, error) {
return false, nil return false, nil
} }


if hs.session.useEMS != c.useEMS {
return false, errors.New("differing EMS state")
}

if hs.session.vers != c.vers { if hs.session.vers != c.vers {
c.sendAlert(alertHandshakeFailure) c.sendAlert(alertHandshakeFailure)
return false, errors.New("tls: server resumed a session with a different version") return false, errors.New("tls: server resumed a session with a different version")
@@ -747,6 +813,7 @@ func (hs *clientHandshakeState) readSessionTicket() error {
masterSecret: hs.masterSecret, masterSecret: hs.masterSecret,
serverCertificates: c.peerCertificates, serverCertificates: c.peerCertificates,
verifiedChains: c.verifiedChains, verifiedChains: c.verifiedChains,
useEMS: c.useEMS,
} }


return nil return nil


+ 39
- 0
handshake_client_test.go View File

@@ -1581,3 +1581,42 @@ func TestGetClientCertificate(t *testing.T) {
} }
} }
} }

func TestRSAPSSKeyError(t *testing.T) {
// crypto/tls does not support the rsa_pss_pss_xxx SignatureSchemes. If support for
// public keys with OID RSASSA-PSS is added to crypto/x509, they will be misused with
// the rsa_pss_rsae_xxx SignatureSchemes. Assert that RSASSA-PSS certificates don't
// parse, or that they don't carry *rsa.PublicKey keys.
b, _ := pem.Decode([]byte(`
-----BEGIN CERTIFICATE-----
MIIDZTCCAhygAwIBAgIUCF2x0FyTgZG0CC9QTDjGWkB5vgEwPgYJKoZIhvcNAQEK
MDGgDTALBglghkgBZQMEAgGhGjAYBgkqhkiG9w0BAQgwCwYJYIZIAWUDBAIBogQC
AgDeMBIxEDAOBgNVBAMMB1JTQS1QU1MwHhcNMTgwNjI3MjI0NDM2WhcNMTgwNzI3
MjI0NDM2WjASMRAwDgYDVQQDDAdSU0EtUFNTMIIBIDALBgkqhkiG9w0BAQoDggEP
ADCCAQoCggEBANxDm0f76JdI06YzsjB3AmmjIYkwUEGxePlafmIASFjDZl/elD0Z
/a7xLX468b0qGxLS5al7XCcEprSdsDR6DF5L520+pCbpfLyPOjuOvGmk9KzVX4x5
b05YXYuXdsQ0Kjxcx2i3jjCday6scIhMJVgBZxTEyMj1thPQM14SHzKCd/m6HmCL
QmswpH2yMAAcBRWzRpp/vdH5DeOJEB3aelq7094no731mrLUCHRiZ1htq8BDB3ou
czwqgwspbqZ4dnMXl2MvfySQ5wJUxQwILbiuAKO2lVVPUbFXHE9pgtznNoPvKwQT
JNcX8ee8WIZc2SEGzofjk3NpjR+2ADB2u3sCAwEAAaNTMFEwHQYDVR0OBBYEFNEz
AdyJ2f+fU+vSCS6QzohnOnprMB8GA1UdIwQYMBaAFNEzAdyJ2f+fU+vSCS6Qzohn
OnprMA8GA1UdEwEB/wQFMAMBAf8wPgYJKoZIhvcNAQEKMDGgDTALBglghkgBZQME
AgGhGjAYBgkqhkiG9w0BAQgwCwYJYIZIAWUDBAIBogQCAgDeA4IBAQCjEdrR5aab
sZmCwrMeKidXgfkmWvfuLDE+TCbaqDZp7BMWcMQXT9O0UoUT5kqgKj2ARm2pEW0Z
H3Z1vj3bbds72qcDIJXp+l0fekyLGeCrX/CbgnMZXEP7+/+P416p34ChR1Wz4dU1
KD3gdsUuTKKeMUog3plxlxQDhRQmiL25ygH1LmjLd6dtIt0GVRGr8lj3euVeprqZ
bZ3Uq5eLfsn8oPgfC57gpO6yiN+UURRTlK3bgYvLh4VWB3XXk9UaQZ7Mq1tpXjoD
HYFybkWzibkZp4WRo+Fa28rirH+/wHt0vfeN7UCceURZEx4JaxIIfe4ku7uDRhJi
RwBA9Xk1KBNF
-----END CERTIFICATE-----`))
if b == nil {
t.Fatal("Failed to decode certificate")
}
cert, err := x509.ParseCertificate(b.Bytes)
if err != nil {
return
}
if _, ok := cert.PublicKey.(*rsa.PublicKey); ok {
t.Error("A RSA-PSS certificate was parsed like a PKCS1 one, and it will be mistakenly used with rsa_pss_rsae_xxx signature algorithms")
}
}

+ 199
- 121
handshake_messages.go View File

@@ -6,81 +6,100 @@ package tls


import ( import (
"bytes" "bytes"
"encoding/binary"
"strings" "strings"
) )


type clientHelloMsg struct {
raw []byte
rawTruncated []byte // for PSK binding
vers uint16
random []byte
sessionId []byte
cipherSuites []uint16
compressionMethods []uint8
nextProtoNeg bool
serverName string
ocspStapling bool
scts bool
supportedCurves []CurveID
supportedPoints []uint8
ticketSupported bool
sessionTicket []uint8
supportedSignatureAlgorithms []SignatureScheme
secureRenegotiation []byte
secureRenegotiationSupported bool
alpnProtocols []string
keyShares []keyShare
supportedVersions []uint16
psks []psk
pskKeyExchangeModes []uint8
earlyData bool
}

// Helpers

// Marshalling of signature_algorithms extension see https://tools.ietf.org/html/rfc5246#section-7.4.1.4.1
// for more details. Extension is serialized in data buffer
// Function advances data slice and returns it, so that it can be used for further processing
func marshalExtensionSignatureAlgorithms(data []byte, sigSchemes []SignatureScheme) []byte {
data[0] = byte(extensionSignatureAlgorithms >> 8)
data[1] = byte(extensionSignatureAlgorithms)
l := 2 + 2*len(sigSchemes)
data[2] = byte(l >> 8)
data[3] = byte(l)
data = data[4:]
// signAlgosCertList helper function returns either list of signature algorithms in case
// signature_algorithms_cert extension should be marshalled or nil in the other case.
// signAlgos is a list of algorithms from signature_algorithms extension. signAlgosCert is a list
// of algorithms from signature_algorithms_cert extension.
func signAlgosCertList(signAlgos, signAlgosCert []SignatureScheme) []SignatureScheme {
if eqSignatureAlgorithms(signAlgos, signAlgosCert) {
// ensure that only supported_algorithms extension is send if supported_algorithms_cert
// has identical content
return nil
}
return signAlgosCert
}


l -= 2
data[0] = byte(l >> 8)
data[1] = byte(l)
type clientHelloMsg struct {
raw []byte
rawTruncated []byte // for PSK binding
vers uint16
random []byte
sessionId []byte
cipherSuites []uint16
compressionMethods []uint8
nextProtoNeg bool
serverName string
ocspStapling bool
scts bool
supportedCurves []CurveID
supportedPoints []uint8
ticketSupported bool
sessionTicket []uint8
supportedSignatureAlgorithms []SignatureScheme
supportedSignatureAlgorithmsCert []SignatureScheme
secureRenegotiation []byte
secureRenegotiationSupported bool
alpnProtocols []string
keyShares []keyShare
supportedVersions []uint16
psks []psk
pskKeyExchangeModes []uint8
earlyData bool
delegatedCredential bool
extendedMSSupported bool // RFC7627
}

// Function used for signature_algorithms and signature_algorithrms_cert
// extensions only (for more details, see TLS 1.3 draft 28, 4.2.3).
//
// It advances data slice and returns it, so that it can be used for further
// processing
func marshalExtensionSignatureAlgorithms(extension uint16, data []byte, schemes []SignatureScheme) []byte {
algNum := uint16(len(schemes))
if algNum == 0 {
return data
}

binary.BigEndian.PutUint16(data, extension)
data = data[2:] data = data[2:]
for _, sigAlgo := range sigSchemes {
data[0] = byte(sigAlgo >> 8)
data[1] = byte(sigAlgo)
binary.BigEndian.PutUint16(data, (2*algNum)+2) // +1 for length
data = data[2:]
binary.BigEndian.PutUint16(data, (2 * algNum))
data = data[2:]

for _, algo := range schemes {
binary.BigEndian.PutUint16(data, uint16(algo))
data = data[2:] data = data[2:]
} }
return data return data
} }


// Unmrshalling of signature_algorithms extension see https://tools.ietf.org/html/rfc5246#section-7.4.1.4.1
// for more details.
// Function used for unmarshalling signature_algorithms or signature_algorithms_cert extensions only
// (for more details, see TLS 1.3 draft 28, 4.2.3)
// In case of error function returns alertDecoderError otherwise filled SignatureScheme slice and alertSuccess // In case of error function returns alertDecoderError otherwise filled SignatureScheme slice and alertSuccess
func unmarshalExtensionSignatureAlgorithms(data []byte, length int) ([]SignatureScheme, alert) { func unmarshalExtensionSignatureAlgorithms(data []byte, length int) ([]SignatureScheme, alert) {

if length < 2 || length&1 != 0 { if length < 2 || length&1 != 0 {
return nil, alertDecodeError return nil, alertDecodeError
} }


l := int(data[0])<<8 | int(data[1])
if l != length-2 {
algLen := binary.BigEndian.Uint16(data)
idx := 2

if int(algLen) != length-2 {
return nil, alertDecodeError return nil, alertDecodeError
} }
n := l / 2
d := data[2:]
sigSchemes := make([]SignatureScheme, n)
for i := range sigSchemes {
sigSchemes[i] = SignatureScheme(d[0])<<8 | SignatureScheme(d[1])
d = d[2:]

schemes := make([]SignatureScheme, algLen/2)
for i := range schemes {
schemes[i] = SignatureScheme(binary.BigEndian.Uint16(data[idx:]))
idx += 2
} }
return sigSchemes, alertSuccess
return schemes, alertSuccess
} }


func (m *clientHelloMsg) equal(i interface{}) bool { func (m *clientHelloMsg) equal(i interface{}) bool {
@@ -104,12 +123,15 @@ func (m *clientHelloMsg) equal(i interface{}) bool {
m.ticketSupported == m1.ticketSupported && m.ticketSupported == m1.ticketSupported &&
bytes.Equal(m.sessionTicket, m1.sessionTicket) && bytes.Equal(m.sessionTicket, m1.sessionTicket) &&
eqSignatureAlgorithms(m.supportedSignatureAlgorithms, m1.supportedSignatureAlgorithms) && eqSignatureAlgorithms(m.supportedSignatureAlgorithms, m1.supportedSignatureAlgorithms) &&
eqSignatureAlgorithms(m.supportedSignatureAlgorithmsCert, m1.supportedSignatureAlgorithmsCert) &&
m.secureRenegotiationSupported == m1.secureRenegotiationSupported && m.secureRenegotiationSupported == m1.secureRenegotiationSupported &&
bytes.Equal(m.secureRenegotiation, m1.secureRenegotiation) && bytes.Equal(m.secureRenegotiation, m1.secureRenegotiation) &&
eqStrings(m.alpnProtocols, m1.alpnProtocols) && eqStrings(m.alpnProtocols, m1.alpnProtocols) &&
eqKeyShares(m.keyShares, m1.keyShares) && eqKeyShares(m.keyShares, m1.keyShares) &&
eqUint16s(m.supportedVersions, m1.supportedVersions) && eqUint16s(m.supportedVersions, m1.supportedVersions) &&
m.earlyData == m1.earlyData
m.earlyData == m1.earlyData &&
m.delegatedCredential == m1.delegatedCredential &&
m.extendedMSSupported == m1.extendedMSSupported
} }


func (m *clientHelloMsg) marshal() []byte { func (m *clientHelloMsg) marshal() []byte {
@@ -120,6 +142,7 @@ func (m *clientHelloMsg) marshal() []byte {
length := 2 + 32 + 1 + len(m.sessionId) + 2 + len(m.cipherSuites)*2 + 1 + len(m.compressionMethods) length := 2 + 32 + 1 + len(m.sessionId) + 2 + len(m.cipherSuites)*2 + 1 + len(m.compressionMethods)
numExtensions := 0 numExtensions := 0
extensionsLength := 0 extensionsLength := 0

if m.nextProtoNeg { if m.nextProtoNeg {
numExtensions++ numExtensions++
} }
@@ -147,6 +170,10 @@ func (m *clientHelloMsg) marshal() []byte {
extensionsLength += 2 + 2*len(m.supportedSignatureAlgorithms) extensionsLength += 2 + 2*len(m.supportedSignatureAlgorithms)
numExtensions++ numExtensions++
} }
if m.getSignatureAlgorithmsCert() != nil {
extensionsLength += 2 + 2*len(m.getSignatureAlgorithmsCert())
numExtensions++
}
if m.secureRenegotiationSupported { if m.secureRenegotiationSupported {
extensionsLength += 1 + len(m.secureRenegotiation) extensionsLength += 1 + len(m.secureRenegotiation)
numExtensions++ numExtensions++
@@ -179,6 +206,12 @@ func (m *clientHelloMsg) marshal() []byte {
if m.earlyData { if m.earlyData {
numExtensions++ numExtensions++
} }
if m.delegatedCredential {
numExtensions++
}
if m.extendedMSSupported {
numExtensions++
}
if numExtensions > 0 { if numExtensions > 0 {
extensionsLength += 4 * numExtensions extensionsLength += 4 * numExtensions
length += 2 + extensionsLength length += 2 + extensionsLength
@@ -305,26 +338,15 @@ func (m *clientHelloMsg) marshal() []byte {
copy(z, m.sessionTicket) copy(z, m.sessionTicket)
z = z[len(m.sessionTicket):] z = z[len(m.sessionTicket):]
} }
if len(m.supportedSignatureAlgorithms) > 0 {
// https://tools.ietf.org/html/rfc5246#section-7.4.1.4.1
// https://tools.ietf.org/html/draft-ietf-tls-tls13-18#section-4.2.3
z[0] = byte(extensionSignatureAlgorithms >> 8)
z[1] = byte(extensionSignatureAlgorithms)
l := 2 + 2*len(m.supportedSignatureAlgorithms)
z[2] = byte(l >> 8)
z[3] = byte(l)
z = z[4:]


l -= 2
z[0] = byte(l >> 8)
z[1] = byte(l)
z = z[2:]
for _, sigAlgo := range m.supportedSignatureAlgorithms {
z[0] = byte(sigAlgo >> 8)
z[1] = byte(sigAlgo)
z = z[2:]
}
if len(m.supportedSignatureAlgorithms) > 0 {
z = marshalExtensionSignatureAlgorithms(extensionSignatureAlgorithms, z, m.supportedSignatureAlgorithms)
}
if m.getSignatureAlgorithmsCert() != nil {
// Ensure only one list of algorithms is sent if supported_algorithms and supported_algorithms_cert are the same
z = marshalExtensionSignatureAlgorithms(extensionSignatureAlgorithmsCert, z, m.getSignatureAlgorithmsCert())
} }

if m.secureRenegotiationSupported { if m.secureRenegotiationSupported {
z[0] = byte(extensionRenegotiationInfo >> 8) z[0] = byte(extensionRenegotiationInfo >> 8)
z[1] = byte(extensionRenegotiationInfo & 0xff) z[1] = byte(extensionRenegotiationInfo & 0xff)
@@ -407,6 +429,14 @@ func (m *clientHelloMsg) marshal() []byte {
z[1] = byte(extensionEarlyData) z[1] = byte(extensionEarlyData)
z = z[4:] z = z[4:]
} }
if m.delegatedCredential {
binary.BigEndian.PutUint16(z, extensionDelegatedCredential)
z = z[4:]
}
if m.extendedMSSupported {
binary.BigEndian.PutUint16(z, extensionEMS)
z = z[4:]
}


m.raw = x m.raw = x


@@ -471,6 +501,8 @@ func (m *clientHelloMsg) unmarshal(data []byte) alert {
m.psks = nil m.psks = nil
m.pskKeyExchangeModes = nil m.pskKeyExchangeModes = nil
m.earlyData = false m.earlyData = false
m.delegatedCredential = false
m.extendedMSSupported = false


if len(data) == 0 { if len(data) == 0 {
// ClientHello is optionally followed by extension data // ClientHello is optionally followed by extension data
@@ -626,7 +658,7 @@ func (m *clientHelloMsg) unmarshal(data []byte) alert {
return alertDecodeError return alertDecodeError
} }
case extensionKeyShare: case extensionKeyShare:
// https://tools.ietf.org/html/draft-ietf-tls-tls13-18#section-4.2.5
// https://tools.ietf.org/html/rfc8446#section-4.2.8
if length < 2 { if length < 2 {
return alertDecodeError return alertDecodeError
} }
@@ -735,6 +767,15 @@ func (m *clientHelloMsg) unmarshal(data []byte) alert {
case extensionEarlyData: case extensionEarlyData:
// https://tools.ietf.org/html/draft-ietf-tls-tls13-18#section-4.2.8 // https://tools.ietf.org/html/draft-ietf-tls-tls13-18#section-4.2.8
m.earlyData = true m.earlyData = true
case extensionDelegatedCredential:
// https://tools.ietf.org/html/draft-ietf-tls-subcerts-02
m.delegatedCredential = true
case extensionEMS:
// RFC 7627
m.extendedMSSupported = true
if length != 0 {
return alertDecodeError
}
} }
data = data[length:] data = data[length:]
bindersOffset += length bindersOffset += length
@@ -743,6 +784,10 @@ func (m *clientHelloMsg) unmarshal(data []byte) alert {
return alertSuccess return alertSuccess
} }


func (m *clientHelloMsg) getSignatureAlgorithmsCert() []SignatureScheme {
return signAlgosCertList(m.supportedSignatureAlgorithms, m.supportedSignatureAlgorithmsCert)
}

type serverHelloMsg struct { type serverHelloMsg struct {
raw []byte raw []byte
vers uint16 vers uint16
@@ -763,6 +808,9 @@ type serverHelloMsg struct {
keyShare keyShare keyShare keyShare
psk bool psk bool
pskIdentity uint16 pskIdentity uint16

// RFC7627
extendedMSSupported bool
} }


func (m *serverHelloMsg) equal(i interface{}) bool { func (m *serverHelloMsg) equal(i interface{}) bool {
@@ -796,7 +844,8 @@ func (m *serverHelloMsg) equal(i interface{}) bool {
m.keyShare.group == m1.keyShare.group && m.keyShare.group == m1.keyShare.group &&
bytes.Equal(m.keyShare.data, m1.keyShare.data) && bytes.Equal(m.keyShare.data, m1.keyShare.data) &&
m.psk == m1.psk && m.psk == m1.psk &&
m.pskIdentity == m1.pskIdentity
m.pskIdentity == m1.pskIdentity &&
m.extendedMSSupported == m1.extendedMSSupported
} }


func (m *serverHelloMsg) marshal() []byte { func (m *serverHelloMsg) marshal() []byte {
@@ -804,12 +853,7 @@ func (m *serverHelloMsg) marshal() []byte {
return m.raw return m.raw
} }


oldTLS13Draft := m.vers >= VersionTLS13Draft18 && m.vers <= VersionTLS13Draft21
length := 38 + len(m.sessionId) length := 38 + len(m.sessionId)
if oldTLS13Draft {
// no compression method, no session ID.
length = 36
}
numExtensions := 0 numExtensions := 0
extensionsLength := 0 extensionsLength := 0


@@ -832,6 +876,9 @@ func (m *serverHelloMsg) marshal() []byte {
extensionsLength += 1 + len(m.secureRenegotiation) extensionsLength += 1 + len(m.secureRenegotiation)
numExtensions++ numExtensions++
} }
if m.extendedMSSupported {
numExtensions++
}
if alpnLen := len(m.alpnProtocol); alpnLen > 0 { if alpnLen := len(m.alpnProtocol); alpnLen > 0 {
if alpnLen >= 256 { if alpnLen >= 256 {
panic("invalid ALPN protocol") panic("invalid ALPN protocol")
@@ -880,20 +927,13 @@ func (m *serverHelloMsg) marshal() []byte {
} }
copy(x[6:38], m.random) copy(x[6:38], m.random)
z := x[38:] z := x[38:]
if !oldTLS13Draft {
x[38] = uint8(len(m.sessionId))
copy(x[39:39+len(m.sessionId)], m.sessionId)
z = x[39+len(m.sessionId):]
}
x[38] = uint8(len(m.sessionId))
copy(x[39:39+len(m.sessionId)], m.sessionId)
z = x[39+len(m.sessionId):]
z[0] = uint8(m.cipherSuite >> 8) z[0] = uint8(m.cipherSuite >> 8)
z[1] = uint8(m.cipherSuite) z[1] = uint8(m.cipherSuite)
if oldTLS13Draft {
// no compression method in older TLS 1.3 drafts.
z = z[2:]
} else {
z[2] = m.compressionMethod
z = z[3:]
}
z[2] = m.compressionMethod
z = z[3:]


if numExtensions > 0 { if numExtensions > 0 {
z[0] = byte(extensionsLength >> 8) z[0] = byte(extensionsLength >> 8)
@@ -976,7 +1016,6 @@ func (m *serverHelloMsg) marshal() []byte {
z = z[len(sct)+2:] z = z[len(sct)+2:]
} }
} }

if m.keyShare.group != 0 { if m.keyShare.group != 0 {
z[0] = uint8(extensionKeyShare >> 8) z[0] = uint8(extensionKeyShare >> 8)
z[1] = uint8(extensionKeyShare) z[1] = uint8(extensionKeyShare)
@@ -1000,6 +1039,10 @@ func (m *serverHelloMsg) marshal() []byte {
z[5] = byte(m.pskIdentity) z[5] = byte(m.pskIdentity)
z = z[6:] z = z[6:]
} }
if m.extendedMSSupported {
binary.BigEndian.PutUint16(z, extensionEMS)
z = z[4:]
}


m.raw = x m.raw = x


@@ -1012,29 +1055,19 @@ func (m *serverHelloMsg) unmarshal(data []byte) alert {
} }
m.raw = data m.raw = data
m.vers = uint16(data[4])<<8 | uint16(data[5]) m.vers = uint16(data[4])<<8 | uint16(data[5])
oldTLS13Draft := m.vers >= VersionTLS13Draft18 && m.vers <= VersionTLS13Draft21
m.random = data[6:38] m.random = data[6:38]
if !oldTLS13Draft {
sessionIdLen := int(data[38])
if sessionIdLen > 32 || len(data) < 39+sessionIdLen {
return alertDecodeError
}
m.sessionId = data[39 : 39+sessionIdLen]
data = data[39+sessionIdLen:]
} else {
data = data[38:]
sessionIdLen := int(data[38])
if sessionIdLen > 32 || len(data) < 39+sessionIdLen {
return alertDecodeError
} }
m.sessionId = data[39 : 39+sessionIdLen]
data = data[39+sessionIdLen:]
if len(data) < 3 { if len(data) < 3 {
return alertDecodeError return alertDecodeError
} }
m.cipherSuite = uint16(data[0])<<8 | uint16(data[1]) m.cipherSuite = uint16(data[0])<<8 | uint16(data[1])
if oldTLS13Draft {
// no compression method in older TLS 1.3 drafts.
data = data[2:]
} else {
m.compressionMethod = data[2]
data = data[3:]
}
m.compressionMethod = data[2]
data = data[3:]


m.nextProtoNeg = false m.nextProtoNeg = false
m.nextProtos = nil m.nextProtos = nil
@@ -1046,6 +1079,7 @@ func (m *serverHelloMsg) unmarshal(data []byte) alert {
m.keyShare.data = nil m.keyShare.data = nil
m.psk = false m.psk = false
m.pskIdentity = 0 m.pskIdentity = 0
m.extendedMSSupported = false


if len(data) == 0 { if len(data) == 0 {
// ServerHello is optionally followed by extension data // ServerHello is optionally followed by extension data
@@ -1069,7 +1103,11 @@ func (m *serverHelloMsg) unmarshal(data []byte) alert {
if m.vers != VersionTLS12 { if m.vers != VersionTLS12 {
return alertDecodeError return alertDecodeError
} }
m.vers = uint16(svData[0])<<8 | uint16(svData[1])
rcvVer := binary.BigEndian.Uint16(svData[0:])
if rcvVer < VersionTLS13 {
return alertIllegalParameter
}
m.vers = rcvVer
} }


for len(data) != 0 { for len(data) != 0 {
@@ -1183,6 +1221,8 @@ func (m *serverHelloMsg) unmarshal(data []byte) alert {
} }
m.psk = true m.psk = true
m.pskIdentity = uint16(data[0])<<8 | uint16(data[1]) m.pskIdentity = uint16(data[0])<<8 | uint16(data[1])
case extensionEMS:
m.extendedMSSupported = true
} }
data = data[length:] data = data[length:]
} }
@@ -1406,9 +1446,10 @@ func (m *certificateMsg) unmarshal(data []byte) alert {
} }


type certificateEntry struct { type certificateEntry struct {
data []byte
ocspStaple []byte
sctList [][]byte
data []byte
ocspStaple []byte
sctList [][]byte
delegatedCredential []byte
} }


type certificateMsg13 struct { type certificateMsg13 struct {
@@ -1430,6 +1471,7 @@ func (m *certificateMsg13) equal(i interface{}) bool {
ok := bytes.Equal(m.certificates[i].data, m1.certificates[i].data) ok := bytes.Equal(m.certificates[i].data, m1.certificates[i].data)
ok = ok && bytes.Equal(m.certificates[i].ocspStaple, m1.certificates[i].ocspStaple) ok = ok && bytes.Equal(m.certificates[i].ocspStaple, m1.certificates[i].ocspStaple)
ok = ok && eqByteSlices(m.certificates[i].sctList, m1.certificates[i].sctList) ok = ok && eqByteSlices(m.certificates[i].sctList, m1.certificates[i].sctList)
ok = ok && bytes.Equal(m.certificates[i].delegatedCredential, m1.certificates[i].delegatedCredential)
if !ok { if !ok {
return false return false
} }
@@ -1456,6 +1498,9 @@ func (m *certificateMsg13) marshal() (x []byte) {
i += 2 + len(sct) i += 2 + len(sct)
} }
} }
if len(cert.delegatedCredential) != 0 {
i += 4 + len(cert.delegatedCredential)
}
} }


length := 3 + 3*len(m.certificates) + i length := 3 + 3*len(m.certificates) + i
@@ -1530,6 +1575,15 @@ func (m *certificateMsg13) marshal() (x []byte) {
sctLenPos[2] = uint8(sctLen >> 8) sctLenPos[2] = uint8(sctLen >> 8)
sctLenPos[3] = uint8(sctLen) sctLenPos[3] = uint8(sctLen)
} }
if len(cert.delegatedCredential) != 0 {
binary.BigEndian.PutUint16(z, extensionDelegatedCredential)
binary.BigEndian.PutUint16(z[2:], uint16(len(cert.delegatedCredential)))
z = z[4:]
copy(z, cert.delegatedCredential)
z = z[len(cert.delegatedCredential):]
extensionLen += 4 + len(cert.delegatedCredential)
}

extLenPos[0] = uint8(extensionLen >> 8) extLenPos[0] = uint8(extensionLen >> 8)
extLenPos[1] = uint8(extensionLen) extLenPos[1] = uint8(extensionLen)
} }
@@ -1635,6 +1689,8 @@ func (m *certificateMsg13) unmarshal(data []byte) alert {
m.certificates[i].sctList = append(m.certificates[i].sctList, body[2:2+sctLen]) m.certificates[i].sctList = append(m.certificates[i].sctList, body[2:2+sctLen])
body = body[2+sctLen:] body = body[2+sctLen:]
} }
case extensionDelegatedCredential:
m.certificates[i].delegatedCredential = body
} }
} }
} }
@@ -2074,15 +2130,17 @@ func (m *certificateRequestMsg) unmarshal(data []byte) alert {
if len(data) != 0 { if len(data) != 0 {
return alertDecodeError return alertDecodeError
} }

return alertSuccess return alertSuccess
} }


type certificateRequestMsg13 struct { type certificateRequestMsg13 struct {
raw []byte raw []byte


requestContext []byte
supportedSignatureAlgorithms []SignatureScheme
certificateAuthorities [][]byte
requestContext []byte
supportedSignatureAlgorithms []SignatureScheme
supportedSignatureAlgorithmsCert []SignatureScheme
certificateAuthorities [][]byte
} }


func (m *certificateRequestMsg13) equal(i interface{}) bool { func (m *certificateRequestMsg13) equal(i interface{}) bool {
@@ -2091,7 +2149,8 @@ func (m *certificateRequestMsg13) equal(i interface{}) bool {
bytes.Equal(m.raw, m1.raw) && bytes.Equal(m.raw, m1.raw) &&
bytes.Equal(m.requestContext, m1.requestContext) && bytes.Equal(m.requestContext, m1.requestContext) &&
eqByteSlices(m.certificateAuthorities, m1.certificateAuthorities) && eqByteSlices(m.certificateAuthorities, m1.certificateAuthorities) &&
eqSignatureAlgorithms(m.supportedSignatureAlgorithms, m1.supportedSignatureAlgorithms)
eqSignatureAlgorithms(m.supportedSignatureAlgorithms, m1.supportedSignatureAlgorithms) &&
eqSignatureAlgorithms(m.supportedSignatureAlgorithmsCert, m1.supportedSignatureAlgorithmsCert)
} }


func (m *certificateRequestMsg13) marshal() (x []byte) { func (m *certificateRequestMsg13) marshal() (x []byte) {
@@ -2104,6 +2163,11 @@ func (m *certificateRequestMsg13) marshal() (x []byte) {
numExtensions := 1 numExtensions := 1
extensionsLength := 2 + 2*len(m.supportedSignatureAlgorithms) extensionsLength := 2 + 2*len(m.supportedSignatureAlgorithms)


if m.getSignatureAlgorithmsCert() != nil {
numExtensions += 1
extensionsLength += 2 + 2*len(m.getSignatureAlgorithmsCert())
}

casLength := 0 casLength := 0
if len(m.certificateAuthorities) > 0 { if len(m.certificateAuthorities) > 0 {
for _, ca := range m.certificateAuthorities { for _, ca := range m.certificateAuthorities {
@@ -2131,7 +2195,11 @@ func (m *certificateRequestMsg13) marshal() (x []byte) {
z = z[2:] z = z[2:]


// TODO: this function should be reused by CH // TODO: this function should be reused by CH
z = marshalExtensionSignatureAlgorithms(z, m.supportedSignatureAlgorithms)
z = marshalExtensionSignatureAlgorithms(extensionSignatureAlgorithms, z, m.supportedSignatureAlgorithms)

if m.getSignatureAlgorithmsCert() != nil {
z = marshalExtensionSignatureAlgorithms(extensionSignatureAlgorithmsCert, z, m.getSignatureAlgorithmsCert())
}
// certificate_authorities // certificate_authorities
if casLength > 0 { if casLength > 0 {
z[0] = byte(extensionCAs >> 8) z[0] = byte(extensionCAs >> 8)
@@ -2204,6 +2272,12 @@ func (m *certificateRequestMsg13) unmarshal(data []byte) alert {
if err != alertSuccess { if err != alertSuccess {
return err return err
} }
case extensionSignatureAlgorithmsCert:
var err alert
m.supportedSignatureAlgorithmsCert, err = unmarshalExtensionSignatureAlgorithms(data, length)
if err != alertSuccess {
return err
}
case extensionCAs: case extensionCAs:
// TODO DRY: share code with CH // TODO DRY: share code with CH
if length < 2 { if length < 2 {
@@ -2240,6 +2314,10 @@ func (m *certificateRequestMsg13) unmarshal(data []byte) alert {
return alertSuccess return alertSuccess
} }


func (m *certificateRequestMsg13) getSignatureAlgorithmsCert() []SignatureScheme {
return signAlgosCertList(m.supportedSignatureAlgorithms, m.supportedSignatureAlgorithmsCert)
}

type certificateVerifyMsg struct { type certificateVerifyMsg struct {
raw []byte raw []byte
hasSignatureAndHash bool hasSignatureAndHash bool


+ 16
- 5
handshake_messages_test.go View File

@@ -162,7 +162,7 @@ func (*clientHelloMsg) Generate(rand *rand.Rand, size int) reflect.Value {
m.keyShares = make([]keyShare, rand.Intn(4)) m.keyShares = make([]keyShare, rand.Intn(4))
for i := range m.keyShares { for i := range m.keyShares {
m.keyShares[i].group = CurveID(rand.Intn(30000)) m.keyShares[i].group = CurveID(rand.Intn(30000))
m.keyShares[i].data = randomBytes(rand.Intn(300), rand)
m.keyShares[i].data = randomBytes(rand.Intn(300)+1, rand)
} }
m.supportedVersions = make([]uint16, rand.Intn(5)) m.supportedVersions = make([]uint16, rand.Intn(5))
for i := range m.supportedVersions { for i := range m.supportedVersions {
@@ -171,7 +171,12 @@ func (*clientHelloMsg) Generate(rand *rand.Rand, size int) reflect.Value {
if rand.Intn(10) > 5 { if rand.Intn(10) > 5 {
m.earlyData = true m.earlyData = true
} }

if rand.Intn(10) > 5 {
m.delegatedCredential = true
}
if rand.Intn(10) > 5 {
m.extendedMSSupported = true
}
return reflect.ValueOf(m) return reflect.ValueOf(m)
} }


@@ -199,19 +204,22 @@ func (*serverHelloMsg) Generate(rand *rand.Rand, size int) reflect.Value {
if rand.Intn(10) > 5 { if rand.Intn(10) > 5 {
m.ticketSupported = true m.ticketSupported = true
} }
if rand.Intn(10) > 5 {
m.extendedMSSupported = true
}
m.alpnProtocol = randomString(rand.Intn(32)+1, rand) m.alpnProtocol = randomString(rand.Intn(32)+1, rand)


if rand.Intn(10) > 5 { if rand.Intn(10) > 5 {
numSCTs := rand.Intn(4) numSCTs := rand.Intn(4)
m.scts = make([][]byte, numSCTs) m.scts = make([][]byte, numSCTs)
for i := range m.scts { for i := range m.scts {
m.scts[i] = randomBytes(rand.Intn(500), rand)
m.scts[i] = randomBytes(rand.Intn(500)+1, rand)
} }
} }


if rand.Intn(10) > 5 { if rand.Intn(10) > 5 {
m.keyShare.group = CurveID(rand.Intn(30000))
m.keyShare.data = randomBytes(rand.Intn(300), rand)
m.keyShare.group = CurveID(rand.Intn(30000) + 1)
m.keyShare.data = randomBytes(rand.Intn(300)+1, rand)
} }
if rand.Intn(10) > 5 { if rand.Intn(10) > 5 {
m.psk = true m.psk = true
@@ -344,6 +352,9 @@ func (*sessionState) Generate(rand *rand.Rand, size int) reflect.Value {
s.vers = uint16(rand.Intn(10000)) s.vers = uint16(rand.Intn(10000))
s.cipherSuite = uint16(rand.Intn(10000)) s.cipherSuite = uint16(rand.Intn(10000))
s.masterSecret = randomBytes(rand.Intn(100), rand) s.masterSecret = randomBytes(rand.Intn(100), rand)
if rand.Intn(10) > 5 {
s.usedEMS = true
}
numCerts := rand.Intn(20) numCerts := rand.Intn(20)
s.certificates = make([][]byte, numCerts) s.certificates = make([][]byte, numCerts)
for i := 0; i < numCerts; i++ { for i := 0; i < numCerts; i++ {


+ 69
- 32
handshake_server.go View File

@@ -16,10 +16,6 @@ import (
"sync/atomic" "sync/atomic"
) )


type Committer interface {
Commit() error
}

// serverHandshakeState contains details of a server handshake in progress. // serverHandshakeState contains details of a server handshake in progress.
// It's discarded once the handshake has completed. // It's discarded once the handshake has completed.
type serverHandshakeState struct { type serverHandshakeState struct {
@@ -30,6 +26,11 @@ type serverHandshakeState struct {
clientHello *clientHelloMsg clientHello *clientHelloMsg
hello *serverHelloMsg hello *serverHelloMsg
cert *Certificate cert *Certificate
privateKey crypto.PrivateKey

// A marshalled DelegatedCredential to be sent to the client in the
// handshake.
delegatedCredential []byte


// TLS 1.0-1.2 fields // TLS 1.0-1.2 fields
ellipticOk bool ellipticOk bool
@@ -276,10 +277,10 @@ Curves:


if len(hs.clientHello.alpnProtocols) > 0 { if len(hs.clientHello.alpnProtocols) > 0 {
if selectedProto, fallback := mutualProtocol(hs.clientHello.alpnProtocols, c.config.NextProtos); !fallback { if selectedProto, fallback := mutualProtocol(hs.clientHello.alpnProtocols, c.config.NextProtos); !fallback {
if hs.hello != nil {
hs.hello.alpnProtocol = selectedProto
} else {
if hs.hello13Enc != nil {
hs.hello13Enc.alpnProtocol = selectedProto hs.hello13Enc.alpnProtocol = selectedProto
} else {
hs.hello.alpnProtocol = selectedProto
} }
c.clientProtocol = selectedProto c.clientProtocol = selectedProto
} }
@@ -299,11 +300,36 @@ Curves:
c.sendAlert(alertInternalError) c.sendAlert(alertInternalError)
return false, err return false, err
} }
if hs.clientHello.scts && hs.hello != nil {

// Set the private key for this handshake to the certificate's secret key.
hs.privateKey = hs.cert.PrivateKey

if hs.clientHello.scts {
hs.hello.scts = hs.cert.SignedCertificateTimestamps hs.hello.scts = hs.cert.SignedCertificateTimestamps
} }


if priv, ok := hs.cert.PrivateKey.(crypto.Signer); ok {
// Set the private key to the DC private key if the client and server are
// willing to negotiate the delegated credential extension.
//
// Check to see if a DelegatedCredential is available and should be used.
// If one is available, the session is using TLS >= 1.2, and the client
// accepts the delegated credential extension, then set the handshake
// private key to the DC private key.
if c.config.GetDelegatedCredential != nil && hs.clientHello.delegatedCredential && c.vers >= VersionTLS12 {
dc, sk, err := c.config.GetDelegatedCredential(hs.clientHelloInfo(), c.vers)
if err != nil {
c.sendAlert(alertInternalError)
return false, err
}

// Set the handshake private key.
if dc != nil {
hs.privateKey = sk
hs.delegatedCredential = dc
}
}

if priv, ok := hs.privateKey.(crypto.Signer); ok {
switch priv.Public().(type) { switch priv.Public().(type) {
case *ecdsa.PublicKey: case *ecdsa.PublicKey:
hs.ecdsaOk = true hs.ecdsaOk = true
@@ -314,7 +340,7 @@ Curves:
return false, fmt.Errorf("tls: unsupported signing key type (%T)", priv.Public()) return false, fmt.Errorf("tls: unsupported signing key type (%T)", priv.Public())
} }
} }
if priv, ok := hs.cert.PrivateKey.(crypto.Decrypter); ok {
if priv, ok := hs.privateKey.(crypto.Decrypter); ok {
switch priv.Public().(type) { switch priv.Public().(type) {
case *rsa.PublicKey: case *rsa.PublicKey:
hs.rsaDecryptOk = true hs.rsaDecryptOk = true
@@ -383,6 +409,11 @@ func (hs *serverHandshakeState) checkForResumption() bool {
return false return false
} }


// Do not resume connections where client support for EMS has changed
if (hs.clientHello.extendedMSSupported && c.config.UseExtendedMasterSecret) != hs.sessionState.usedEMS {
return false
}

cipherSuiteOk := false cipherSuiteOk := false
// Check that the client is still offering the ciphersuite in the session. // Check that the client is still offering the ciphersuite in the session.
for _, id := range hs.clientHello.cipherSuites { for _, id := range hs.clientHello.cipherSuites {
@@ -420,6 +451,7 @@ func (hs *serverHandshakeState) doResumeHandshake() error {
// that we're doing a resumption. // that we're doing a resumption.
hs.hello.sessionId = hs.clientHello.sessionId hs.hello.sessionId = hs.clientHello.sessionId
hs.hello.ticketSupported = hs.sessionState.usedOldKey hs.hello.ticketSupported = hs.sessionState.usedOldKey
hs.hello.extendedMSSupported = hs.clientHello.extendedMSSupported && c.config.UseExtendedMasterSecret
hs.finishedHash = newFinishedHash(c.vers, hs.suite) hs.finishedHash = newFinishedHash(c.vers, hs.suite)
hs.finishedHash.discardHandshakeBuffer() hs.finishedHash.discardHandshakeBuffer()
hs.finishedHash.Write(hs.clientHello.marshal()) hs.finishedHash.Write(hs.clientHello.marshal())
@@ -435,6 +467,7 @@ func (hs *serverHandshakeState) doResumeHandshake() error {
} }


hs.masterSecret = hs.sessionState.masterSecret hs.masterSecret = hs.sessionState.masterSecret
c.useEMS = hs.sessionState.usedEMS


return nil return nil
} }
@@ -448,6 +481,7 @@ func (hs *serverHandshakeState) doFullHandshake() error {


hs.hello.ticketSupported = hs.clientHello.ticketSupported && !c.config.SessionTicketsDisabled hs.hello.ticketSupported = hs.clientHello.ticketSupported && !c.config.SessionTicketsDisabled
hs.hello.cipherSuite = hs.suite.id hs.hello.cipherSuite = hs.suite.id
hs.hello.extendedMSSupported = hs.clientHello.extendedMSSupported && c.config.UseExtendedMasterSecret


hs.finishedHash = newFinishedHash(hs.c.vers, hs.suite) hs.finishedHash = newFinishedHash(hs.c.vers, hs.suite)
if c.config.ClientAuth == NoClientCert { if c.config.ClientAuth == NoClientCert {
@@ -479,7 +513,7 @@ func (hs *serverHandshakeState) doFullHandshake() error {
} }


keyAgreement := hs.suite.ka(c.vers) keyAgreement := hs.suite.ka(c.vers)
skx, err := keyAgreement.generateServerKeyExchange(c.config, hs.cert, hs.clientHello, hs.hello)
skx, err := keyAgreement.generateServerKeyExchange(c.config, hs.privateKey, hs.clientHello, hs.hello)
if err != nil { if err != nil {
c.sendAlert(alertHandshakeFailure) c.sendAlert(alertHandshakeFailure)
return err return err
@@ -544,15 +578,6 @@ func (hs *serverHandshakeState) doFullHandshake() error {
} }
hs.finishedHash.Write(certMsg.marshal()) hs.finishedHash.Write(certMsg.marshal())


if len(certMsg.certificates) == 0 {
// The client didn't actually send a certificate
switch c.config.ClientAuth {
case RequireAnyClientCert, RequireAndVerifyClientCert:
c.sendAlert(alertBadCertificate)
return errors.New("tls: client didn't provide a certificate")
}
}

pub, err = hs.processCertsFromClient(certMsg.certificates) pub, err = hs.processCertsFromClient(certMsg.certificates)
if err != nil { if err != nil {
return err return err
@@ -572,7 +597,7 @@ func (hs *serverHandshakeState) doFullHandshake() error {
} }
hs.finishedHash.Write(ckx.marshal()) hs.finishedHash.Write(ckx.marshal())


preMasterSecret, err := keyAgreement.processClientKeyExchange(c.config, hs.cert, ckx, c.vers)
preMasterSecret, err := keyAgreement.processClientKeyExchange(c.config, hs.privateKey, ckx, c.vers)
if err != nil { if err != nil {
if err == errClientKeyExchange { if err == errClientKeyExchange {
c.sendAlert(alertDecodeError) c.sendAlert(alertDecodeError)
@@ -581,7 +606,8 @@ func (hs *serverHandshakeState) doFullHandshake() error {
} }
return err return err
} }
hs.masterSecret = masterFromPreMasterSecret(c.vers, hs.suite, preMasterSecret, hs.clientHello.random, hs.hello.random)
c.useEMS = hs.hello.extendedMSSupported
hs.masterSecret = masterFromPreMasterSecret(c.vers, hs.suite, preMasterSecret, hs.clientHello.random, hs.hello.random, hs.finishedHash, c.useEMS)
if err := c.config.writeKeyLog("CLIENT_RANDOM", hs.clientHello.random, hs.masterSecret); err != nil { if err := c.config.writeKeyLog("CLIENT_RANDOM", hs.clientHello.random, hs.masterSecret); err != nil {
c.sendAlert(alertInternalError) c.sendAlert(alertInternalError)
return err return err
@@ -711,6 +737,7 @@ func (hs *serverHandshakeState) sendSessionTicket() error {
cipherSuite: hs.suite.id, cipherSuite: hs.suite.id,
masterSecret: hs.masterSecret, masterSecret: hs.masterSecret,
certificates: hs.certsFromClient, certificates: hs.certsFromClient,
usedEMS: c.useEMS,
} }
m.ticket, err = c.encryptTicket(state.marshal()) m.ticket, err = c.encryptTicket(state.marshal())
if err != nil { if err != nil {
@@ -751,6 +778,15 @@ func (hs *serverHandshakeState) sendFinished(out []byte) error {
func (hs *serverHandshakeState) processCertsFromClient(certificates [][]byte) (crypto.PublicKey, error) { func (hs *serverHandshakeState) processCertsFromClient(certificates [][]byte) (crypto.PublicKey, error) {
c := hs.c c := hs.c


if len(certificates) == 0 {
// The client didn't actually send a certificate
switch c.config.ClientAuth {
case RequireAnyClientCert, RequireAndVerifyClientCert:
c.sendAlert(alertBadCertificate)
return nil, errors.New("tls: client didn't provide a certificate")
}
}

hs.certsFromClient = certificates hs.certsFromClient = certificates
certs := make([]*x509.Certificate, len(certificates)) certs := make([]*x509.Certificate, len(certificates))
var err error var err error
@@ -880,16 +916,17 @@ func (hs *serverHandshakeState) clientHelloInfo() *ClientHelloInfo {
} }


hs.cachedClientHelloInfo = &ClientHelloInfo{ hs.cachedClientHelloInfo = &ClientHelloInfo{
CipherSuites: hs.clientHello.cipherSuites,
ServerName: hs.clientHello.serverName,
SupportedCurves: hs.clientHello.supportedCurves,
SupportedPoints: hs.clientHello.supportedPoints,
SignatureSchemes: hs.clientHello.supportedSignatureAlgorithms,
SupportedProtos: hs.clientHello.alpnProtocols,
SupportedVersions: supportedVersions,
Conn: hs.c.conn,
Offered0RTTData: hs.clientHello.earlyData,
Fingerprint: pskBinder,
CipherSuites: hs.clientHello.cipherSuites,
ServerName: hs.clientHello.serverName,
SupportedCurves: hs.clientHello.supportedCurves,
SupportedPoints: hs.clientHello.supportedPoints,
SignatureSchemes: hs.clientHello.supportedSignatureAlgorithms,
SupportedProtos: hs.clientHello.alpnProtocols,
SupportedVersions: supportedVersions,
Conn: hs.c.conn,
Offered0RTTData: hs.clientHello.earlyData,
AcceptsDelegatedCredential: hs.clientHello.delegatedCredential,
Fingerprint: pskBinder,
} }


return hs.cachedClientHelloInfo return hs.cachedClientHelloInfo


+ 0
- 2
handshake_server_test.go View File

@@ -394,8 +394,6 @@ func TestSCTHandshake(t *testing.T) {
PrivateKey: testRSAPrivateKey, PrivateKey: testRSAPrivateKey,
SignedCertificateTimestamps: expected, SignedCertificateTimestamps: expected,
}}, }},
// See GH#76
MaxVersion: VersionTLS12,
} }
clientConfig := &Config{ clientConfig := &Config{
InsecureSkipVerify: true, InsecureSkipVerify: true,


+ 13
- 14
key_agreement.go View File

@@ -10,7 +10,6 @@ import (
"crypto/md5" "crypto/md5"
"crypto/rsa" "crypto/rsa"
"crypto/sha1" "crypto/sha1"
"crypto/x509"
"errors" "errors"
"io" "io"
"math/big" "math/big"
@@ -25,11 +24,11 @@ var errServerKeyExchange = errors.New("tls: invalid ServerKeyExchange message")
// encrypts the pre-master secret to the server's public key. // encrypts the pre-master secret to the server's public key.
type rsaKeyAgreement struct{} type rsaKeyAgreement struct{}


func (ka rsaKeyAgreement) generateServerKeyExchange(config *Config, cert *Certificate, clientHello *clientHelloMsg, hello *serverHelloMsg) (*serverKeyExchangeMsg, error) {
func (ka rsaKeyAgreement) generateServerKeyExchange(config *Config, sk crypto.PrivateKey, clientHello *clientHelloMsg, hello *serverHelloMsg) (*serverKeyExchangeMsg, error) {
return nil, nil return nil, nil
} }


func (ka rsaKeyAgreement) processClientKeyExchange(config *Config, cert *Certificate, ckx *clientKeyExchangeMsg, version uint16) ([]byte, error) {
func (ka rsaKeyAgreement) processClientKeyExchange(config *Config, sk crypto.PrivateKey, ckx *clientKeyExchangeMsg, version uint16) ([]byte, error) {
if len(ckx.ciphertext) < 2 { if len(ckx.ciphertext) < 2 {
return nil, errClientKeyExchange return nil, errClientKeyExchange
} }
@@ -42,7 +41,7 @@ func (ka rsaKeyAgreement) processClientKeyExchange(config *Config, cert *Certifi
} }
ciphertext = ckx.ciphertext[2:] ciphertext = ckx.ciphertext[2:]
} }
priv, ok := cert.PrivateKey.(crypto.Decrypter)
priv, ok := sk.(crypto.Decrypter)
if !ok { if !ok {
return nil, errors.New("tls: certificate private key does not implement crypto.Decrypter") return nil, errors.New("tls: certificate private key does not implement crypto.Decrypter")
} }
@@ -60,11 +59,11 @@ func (ka rsaKeyAgreement) processClientKeyExchange(config *Config, cert *Certifi
return preMasterSecret, nil return preMasterSecret, nil
} }


func (ka rsaKeyAgreement) processServerKeyExchange(config *Config, clientHello *clientHelloMsg, serverHello *serverHelloMsg, cert *x509.Certificate, skx *serverKeyExchangeMsg) error {
func (ka rsaKeyAgreement) processServerKeyExchange(config *Config, clientHello *clientHelloMsg, serverHello *serverHelloMsg, pk crypto.PublicKey, skx *serverKeyExchangeMsg) error {
return errors.New("tls: unexpected ServerKeyExchange") return errors.New("tls: unexpected ServerKeyExchange")
} }


func (ka rsaKeyAgreement) generateClientKeyExchange(config *Config, clientHello *clientHelloMsg, cert *x509.Certificate) ([]byte, *clientKeyExchangeMsg, error) {
func (ka rsaKeyAgreement) generateClientKeyExchange(config *Config, clientHello *clientHelloMsg, pk crypto.PublicKey) ([]byte, *clientKeyExchangeMsg, error) {
preMasterSecret := make([]byte, 48) preMasterSecret := make([]byte, 48)
preMasterSecret[0] = byte(clientHello.vers >> 8) preMasterSecret[0] = byte(clientHello.vers >> 8)
preMasterSecret[1] = byte(clientHello.vers) preMasterSecret[1] = byte(clientHello.vers)
@@ -73,7 +72,7 @@ func (ka rsaKeyAgreement) generateClientKeyExchange(config *Config, clientHello
return nil, nil, err return nil, nil, err
} }


encrypted, err := rsa.EncryptPKCS1v15(config.rand(), cert.PublicKey.(*rsa.PublicKey), preMasterSecret)
encrypted, err := rsa.EncryptPKCS1v15(config.rand(), pk.(*rsa.PublicKey), preMasterSecret)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
@@ -156,7 +155,7 @@ type ecdheKeyAgreement struct {
x, y *big.Int x, y *big.Int
} }


func (ka *ecdheKeyAgreement) generateServerKeyExchange(config *Config, cert *Certificate, clientHello *clientHelloMsg, hello *serverHelloMsg) (*serverKeyExchangeMsg, error) {
func (ka *ecdheKeyAgreement) generateServerKeyExchange(config *Config, sk crypto.PrivateKey, clientHello *clientHelloMsg, hello *serverHelloMsg) (*serverKeyExchangeMsg, error) {
preferredCurves := config.curvePreferences() preferredCurves := config.curvePreferences()


NextCandidate: NextCandidate:
@@ -207,7 +206,7 @@ NextCandidate:
serverECDHParams[3] = byte(len(ecdhePublic)) serverECDHParams[3] = byte(len(ecdhePublic))
copy(serverECDHParams[4:], ecdhePublic) copy(serverECDHParams[4:], ecdhePublic)


priv, ok := cert.PrivateKey.(crypto.Signer)
priv, ok := sk.(crypto.Signer)
if !ok { if !ok {
return nil, errors.New("tls: certificate private key does not implement crypto.Signer") return nil, errors.New("tls: certificate private key does not implement crypto.Signer")
} }
@@ -255,7 +254,7 @@ NextCandidate:
return skx, nil return skx, nil
} }


func (ka *ecdheKeyAgreement) processClientKeyExchange(config *Config, cert *Certificate, ckx *clientKeyExchangeMsg, version uint16) ([]byte, error) {
func (ka *ecdheKeyAgreement) processClientKeyExchange(config *Config, sk crypto.PrivateKey, ckx *clientKeyExchangeMsg, version uint16) ([]byte, error) {
if len(ckx.ciphertext) == 0 || int(ckx.ciphertext[0]) != len(ckx.ciphertext)-1 { if len(ckx.ciphertext) == 0 || int(ckx.ciphertext[0]) != len(ckx.ciphertext)-1 {
return nil, errClientKeyExchange return nil, errClientKeyExchange
} }
@@ -291,7 +290,7 @@ func (ka *ecdheKeyAgreement) processClientKeyExchange(config *Config, cert *Cert
return preMasterSecret, nil return preMasterSecret, nil
} }


func (ka *ecdheKeyAgreement) processServerKeyExchange(config *Config, clientHello *clientHelloMsg, serverHello *serverHelloMsg, cert *x509.Certificate, skx *serverKeyExchangeMsg) error {
func (ka *ecdheKeyAgreement) processServerKeyExchange(config *Config, clientHello *clientHelloMsg, serverHello *serverHelloMsg, pk crypto.PublicKey, skx *serverKeyExchangeMsg) error {
if len(skx.key) < 4 { if len(skx.key) < 4 {
return errServerKeyExchange return errServerKeyExchange
} }
@@ -337,7 +336,7 @@ func (ka *ecdheKeyAgreement) processServerKeyExchange(config *Config, clientHell
return errServerKeyExchange return errServerKeyExchange
} }
} }
_, sigType, hashFunc, err := pickSignatureAlgorithm(cert.PublicKey, []SignatureScheme{signatureAlgorithm}, clientHello.supportedSignatureAlgorithms, ka.version)
_, sigType, hashFunc, err := pickSignatureAlgorithm(pk, []SignatureScheme{signatureAlgorithm}, clientHello.supportedSignatureAlgorithms, ka.version)
if err != nil { if err != nil {
return err return err
} }
@@ -355,10 +354,10 @@ func (ka *ecdheKeyAgreement) processServerKeyExchange(config *Config, clientHell
if err != nil { if err != nil {
return err return err
} }
return verifyHandshakeSignature(sigType, cert.PublicKey, hashFunc, digest, sig)
return verifyHandshakeSignature(sigType, pk, hashFunc, digest, sig)
} }


func (ka *ecdheKeyAgreement) generateClientKeyExchange(config *Config, clientHello *clientHelloMsg, cert *x509.Certificate) ([]byte, *clientKeyExchangeMsg, error) {
func (ka *ecdheKeyAgreement) generateClientKeyExchange(config *Config, clientHello *clientHelloMsg, pk crypto.PublicKey) ([]byte, *clientKeyExchangeMsg, error) {
if ka.curveid == 0 { if ka.curveid == 0 {
return nil, nil, errors.New("tls: missing ServerKeyExchange message") return nil, nil, errors.New("tls: missing ServerKeyExchange message")
} }


+ 16
- 8
prf.go View File

@@ -117,6 +117,7 @@ var masterSecretLabel = []byte("master secret")
var keyExpansionLabel = []byte("key expansion") var keyExpansionLabel = []byte("key expansion")
var clientFinishedLabel = []byte("client finished") var clientFinishedLabel = []byte("client finished")
var serverFinishedLabel = []byte("server finished") var serverFinishedLabel = []byte("server finished")
var extendedMasterSecretLabel = []byte("extended master secret")


func prfAndHashForVersion(version uint16, suite *cipherSuite) (func(result, secret, label, seed []byte), crypto.Hash) { func prfAndHashForVersion(version uint16, suite *cipherSuite) (func(result, secret, label, seed []byte), crypto.Hash) {
switch version { switch version {
@@ -141,14 +142,21 @@ func prfForVersion(version uint16, suite *cipherSuite) func(result, secret, labe


// masterFromPreMasterSecret generates the master secret from the pre-master // masterFromPreMasterSecret generates the master secret from the pre-master
// secret. See http://tools.ietf.org/html/rfc5246#section-8.1 // secret. See http://tools.ietf.org/html/rfc5246#section-8.1
func masterFromPreMasterSecret(version uint16, suite *cipherSuite, preMasterSecret, clientRandom, serverRandom []byte) []byte {
seed := make([]byte, 0, len(clientRandom)+len(serverRandom))
seed = append(seed, clientRandom...)
seed = append(seed, serverRandom...)

masterSecret := make([]byte, masterSecretLength)
prfForVersion(version, suite)(masterSecret, preMasterSecret, masterSecretLabel, seed)
return masterSecret
func masterFromPreMasterSecret(version uint16, suite *cipherSuite, preMasterSecret, clientRandom, serverRandom []byte, fin finishedHash, ems bool) []byte {
if ems {
session_hash := fin.Sum()
masterSecret := make([]byte, masterSecretLength)
prfForVersion(version, suite)(masterSecret, preMasterSecret, extendedMasterSecretLabel, session_hash)
return masterSecret
} else {
seed := make([]byte, 0, len(clientRandom)+len(serverRandom))
seed = append(seed, clientRandom...)
seed = append(seed, serverRandom...)

masterSecret := make([]byte, masterSecretLength)
prfForVersion(version, suite)(masterSecret, preMasterSecret, masterSecretLabel, seed)
return masterSecret
}
} }


// keysFromMasterSecret generates the connection keys from the master // keysFromMasterSecret generates the connection keys from the master


+ 2
- 1
prf_test.go View File

@@ -49,8 +49,9 @@ func TestKeysFromPreMasterSecret(t *testing.T) {
in, _ := hex.DecodeString(test.preMasterSecret) in, _ := hex.DecodeString(test.preMasterSecret)
clientRandom, _ := hex.DecodeString(test.clientRandom) clientRandom, _ := hex.DecodeString(test.clientRandom)
serverRandom, _ := hex.DecodeString(test.serverRandom) serverRandom, _ := hex.DecodeString(test.serverRandom)
fin := newFinishedHash(test.version, test.suite)


masterSecret := masterFromPreMasterSecret(test.version, test.suite, in, clientRandom, serverRandom)
masterSecret := masterFromPreMasterSecret(test.version, test.suite, in, clientRandom, serverRandom, fin, false)
if s := hex.EncodeToString(masterSecret); s != test.masterSecret { if s := hex.EncodeToString(masterSecret); s != test.masterSecret {
t.Errorf("#%d: bad master secret %s, want %s", i, s, test.masterSecret) t.Errorf("#%d: bad master secret %s, want %s", i, s, test.masterSecret)
continue continue


+ 392
- 0
subcerts.go View File

@@ -0,0 +1,392 @@
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package tls

// Delegated credentials for TLS
// (https://tools.ietf.org/html/draft-ietf-tls-subcerts-02) is an IETF Internet
// draft and proposed TLS extension. This allows a backend server to delegate
// TLS termination to a trusted frontend. If the client supports this extension,
// then the frontend may use a "delegated credential" as the signing key in the
// handshake. A delegated credential is a short lived key pair delegated to the
// server by an entity trusted by the client. Once issued, credentials can't be
// revoked; in order to mitigate risk in case the frontend is compromised, the
// credential is only valid for a short time (days, hours, or even minutes).
//
// This implements draft 02. This draft doesn't specify an object identifier for
// the X.509 extension; we use one assigned by Cloudflare. In addition, IANA has
// not assigned an extension ID for this extension; we picked up one that's not
// yet taken.
//
// TODO(cjpatton) Only ECDSA is supported with delegated credentials for now;
// we'd like to suppoort for EcDSA signatures once these have better support
// upstream.

import (
"bytes"
"crypto"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/x509"
"encoding/asn1"
"encoding/binary"
"errors"
"fmt"
"time"
)

const (
// length of the public key field
dcPubKeyFieldLen = 3
dcMaxTTLSeconds = 60 * 60 * 24 * 7 // 7 days
dcMaxTTL = time.Duration(dcMaxTTLSeconds * time.Second)
dcMaxPublicKeyLen = 1 << 24 // Bytes
dcMaxSignatureLen = 1 << 16 // Bytes
)

var errNoDelegationUsage = errors.New("certificate not authorized for delegation")

// delegationUsageId is the DelegationUsage X.509 extension OID
//
// NOTE(cjpatton) This OID is a child of Cloudflare's IANA-assigned OID.
var delegationUsageId = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 44363, 44}

// canDelegate returns true if a certificate can be used for delegated
// credentials.
func canDelegate(cert *x509.Certificate) bool {
// Check that the digitalSignature key usage is set.
if (cert.KeyUsage & x509.KeyUsageDigitalSignature) == 0 {
return false
}

// Check that the certificate has the DelegationUsage extension and that
// it's non-critical (per the spec).
for _, extension := range cert.Extensions {
if extension.Id.Equal(delegationUsageId) {
return true
}
}
return false
}

// credential stores the public components of a credential.
type credential struct {
// The serialized form of the credential.
raw []byte

// The amount of time for which the credential is valid. Specifically, the
// the credential expires `ValidTime` seconds after the `notBefore` of the
// delegation certificate. The delegator shall not issue delegated
// credentials that are valid for more than 7 days from the current time.
//
// When this data structure is serialized, this value is converted to a
// uint32 representing the duration in seconds.
validTime time.Duration

// The signature scheme associated with the delegated credential public key.
expectedCertVerifyAlgorithm SignatureScheme

// The version of TLS in which the credential will be used.
expectedVersion uint16

// The credential public key.
publicKey crypto.PublicKey
}

// isExpired returns true if the credential has expired. The end of the validity
// interval is defined as the delegator certificate's notBefore field (`start`)
// plus ValidTime seconds. This function simply checks that the current time
// (`now`) is before the end of the valdity interval.
func (cred *credential) isExpired(start, now time.Time) bool {
end := start.Add(cred.validTime)
return !now.Before(end)
}

// invalidTTL returns true if the credential's validity period is longer than the
// maximum permitted. This is defined by the certificate's notBefore field
// (`start`) plus the ValidTime, minus the current time (`now`).
func (cred *credential) invalidTTL(start, now time.Time) bool {
return cred.validTime > (now.Sub(start) + dcMaxTTL).Round(time.Second)
}

// marshalSubjectPublicKeyInfo returns a DER encoded SubjectPublicKeyInfo structure
// (as defined in the X.509 standard) for the credential.
func (cred *credential) marshalSubjectPublicKeyInfo() ([]byte, error) {
switch cred.expectedCertVerifyAlgorithm {
case ECDSAWithP256AndSHA256,
ECDSAWithP384AndSHA384,
ECDSAWithP521AndSHA512:
serializedPublicKey, err := x509.MarshalPKIXPublicKey(cred.publicKey)
if err != nil {
return nil, err
}
return serializedPublicKey, nil

default:
return nil, fmt.Errorf("unsupported signature scheme: 0x%04x", cred.expectedCertVerifyAlgorithm)
}
}

// marshal encodes a credential in the wire format specified in
// https://tools.ietf.org/html/draft-ietf-tls-subcerts-02.
func (cred *credential) marshal() ([]byte, error) {
// The number of bytes comprising the DC parameters, which includes the
// validity time (4 bytes), the signature scheme of the public key (2 bytes), and
// the protocol version (2 bytes).
paramsLen := 8

// The first 4 bytes are the valid_time, scheme, and version fields.
serialized := make([]byte, paramsLen+dcPubKeyFieldLen)
binary.BigEndian.PutUint32(serialized, uint32(cred.validTime/time.Second))
binary.BigEndian.PutUint16(serialized[4:], uint16(cred.expectedCertVerifyAlgorithm))
binary.BigEndian.PutUint16(serialized[6:], cred.expectedVersion)

// Encode the public key and assert that the encoding is no longer than 2^16
// bytes (per the spec).
serializedPublicKey, err := cred.marshalSubjectPublicKeyInfo()
if err != nil {
return nil, err
}
if len(serializedPublicKey) > dcMaxPublicKeyLen {
return nil, errors.New("public key is too long")
}

// The next 3 bytes are the length of the public key field, which may be up
// to 2^24 bytes long.
putUint24(serialized[paramsLen:], len(serializedPublicKey))

// The remaining bytes are the public key itself.
serialized = append(serialized, serializedPublicKey...)
cred.raw = serialized
return serialized, nil
}

// unmarshalCredential decodes a credential and returns it.
func unmarshalCredential(serialized []byte) (*credential, error) {
// The number of bytes comprising the DC parameters.
paramsLen := 8

if len(serialized) < paramsLen+dcPubKeyFieldLen {
return nil, errors.New("credential is too short")
}

// Parse the valid_time, scheme, and version fields.
validTime := time.Duration(binary.BigEndian.Uint32(serialized)) * time.Second
scheme := SignatureScheme(binary.BigEndian.Uint16(serialized[4:]))
version := binary.BigEndian.Uint16(serialized[6:])

// Parse the SubjectPublicKeyInfo.
pk, err := x509.ParsePKIXPublicKey(serialized[paramsLen+dcPubKeyFieldLen:])
if err != nil {
return nil, err
}

if _, ok := pk.(*ecdsa.PublicKey); !ok {
return nil, fmt.Errorf("unsupported delegation key type: %T", pk)
}

return &credential{
raw: serialized,
validTime: validTime,
expectedCertVerifyAlgorithm: scheme,
expectedVersion: version,
publicKey: pk,
}, nil
}

// getCredentialLen returns the number of bytes comprising the serialized
// credential that starts at the beginning of the input slice. It returns an
// error if the input is too short to contain a credential.
func getCredentialLen(serialized []byte) (int, error) {
paramsLen := 8
if len(serialized) < paramsLen+dcPubKeyFieldLen {
return 0, errors.New("credential is too short")
}
// First several bytes are the valid_time, scheme, and version fields.
serialized = serialized[paramsLen:]

// The next 3 bytes are the length of the serialized public key, which may
// be up to 2^24 bytes in length.
serializedPublicKeyLen := getUint24(serialized)
serialized = serialized[dcPubKeyFieldLen:]

if len(serialized) < serializedPublicKeyLen {
return 0, errors.New("public key of credential is too short")
}

return paramsLen + dcPubKeyFieldLen + serializedPublicKeyLen, nil
}

// delegatedCredential stores a credential and its delegation.
type delegatedCredential struct {
raw []byte

// The credential, which contains a public and its validity time.
cred *credential

// The signature scheme used to sign the credential.
algorithm SignatureScheme

// The credential's delegation.
signature []byte
}

// ensureCertificateHasLeaf parses the leaf certificate if needed.
func ensureCertificateHasLeaf(cert *Certificate) error {
var err error
if cert.Leaf == nil {
if len(cert.Certificate[0]) == 0 {
return errors.New("missing leaf certificate")
}
cert.Leaf, err = x509.ParseCertificate(cert.Certificate[0])
if err != nil {
return err
}
}
return nil
}

// validate checks that that the signature is valid, that the credential hasn't
// expired, and that the TTL is valid. It also checks that certificate can be
// used for delegation.
func (dc *delegatedCredential) validate(cert *x509.Certificate, now time.Time) (bool, error) {
// Check that the cert can delegate.
if !canDelegate(cert) {
return false, errNoDelegationUsage
}

if dc.cred.isExpired(cert.NotBefore, now) {
return false, errors.New("credential has expired")
}

if dc.cred.invalidTTL(cert.NotBefore, now) {
return false, errors.New("credential TTL is invalid")
}

// Prepare the credential for verification.
rawCred, err := dc.cred.marshal()
if err != nil {
return false, err
}
hash := getHash(dc.algorithm)
in := prepareDelegation(hash, rawCred, cert.Raw, dc.algorithm)

// TODO(any) This code overlaps significantly with verifyHandshakeSignature()
// in ../auth.go. This should be refactored.
switch dc.algorithm {
case ECDSAWithP256AndSHA256,
ECDSAWithP384AndSHA384,
ECDSAWithP521AndSHA512:
pk, ok := cert.PublicKey.(*ecdsa.PublicKey)
if !ok {
return false, errors.New("expected ECDSA public key")
}
sig := new(ecdsaSignature)
if _, err = asn1.Unmarshal(dc.signature, sig); err != nil {
return false, err
}
return ecdsa.Verify(pk, in, sig.R, sig.S), nil

default:
return false, fmt.Errorf(
"unsupported signature scheme: 0x%04x", dc.algorithm)
}
}

// unmarshalDelegatedCredential decodes a DelegatedCredential structure.
func unmarshalDelegatedCredential(serialized []byte) (*delegatedCredential, error) {
// Get the length of the serialized credential that begins at the start of
// the input slice.
serializedCredentialLen, err := getCredentialLen(serialized)
if err != nil {
return nil, err
}

// Parse the credential.
cred, err := unmarshalCredential(serialized[:serializedCredentialLen])
if err != nil {
return nil, err
}

// Parse the signature scheme.
serialized = serialized[serializedCredentialLen:]
if len(serialized) < 4 {
return nil, errors.New("delegated credential is too short")
}
scheme := SignatureScheme(binary.BigEndian.Uint16(serialized))

// Parse the signature length.
serialized = serialized[2:]
serializedSignatureLen := binary.BigEndian.Uint16(serialized)

// Prase the signature.
serialized = serialized[2:]
if len(serialized) < int(serializedSignatureLen) {
return nil, errors.New("signature of delegated credential is too short")
}
sig := serialized[:serializedSignatureLen]

return &delegatedCredential{
raw: serialized,
cred: cred,
algorithm: scheme,
signature: sig,
}, nil
}

// getCurve maps the SignatureScheme to its corresponding elliptic.Curve.
func getCurve(scheme SignatureScheme) elliptic.Curve {
switch scheme {
case ECDSAWithP256AndSHA256:
return elliptic.P256()
case ECDSAWithP384AndSHA384:
return elliptic.P384()
case ECDSAWithP521AndSHA512:
return elliptic.P521()
default:
return nil
}
}

// getHash maps the SignatureScheme to its corresponding hash function.
//
// TODO(any) This function overlaps with hashForSignatureScheme in 13.go.
func getHash(scheme SignatureScheme) crypto.Hash {
switch scheme {
case ECDSAWithP256AndSHA256:
return crypto.SHA256
case ECDSAWithP384AndSHA384:
return crypto.SHA384
case ECDSAWithP521AndSHA512:
return crypto.SHA512
default:
return 0 // Unknown hash function
}
}

// prepareDelegation returns a hash of the message that the delegator is to
// sign. The inputs are the credential (`cred`), the DER-encoded delegator
// certificate (`delegatorCert`) and the signature scheme of the delegator
// (`delegatorAlgorithm`).
func prepareDelegation(hash crypto.Hash, cred, delegatorCert []byte, delegatorAlgorithm SignatureScheme) []byte {
h := hash.New()

// The header.
h.Write(bytes.Repeat([]byte{0x20}, 64))
h.Write([]byte("TLS, server delegated credentials"))
h.Write([]byte{0x00})

// The delegation certificate.
h.Write(delegatorCert)

// The credential.
h.Write(cred)

// The delegator signature scheme.
var serializedScheme [2]byte
binary.BigEndian.PutUint16(serializedScheme[:], uint16(delegatorAlgorithm))
h.Write(serializedScheme[:])

return h.Sum(nil)
}

+ 365
- 0
subcerts_test.go View File

@@ -0,0 +1,365 @@
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package tls

import (
"crypto"
"crypto/x509"
"encoding/asn1"
"encoding/pem"
"errors"
"fmt"
"testing"
"time"
)

// A PEM-encoded "delegation certificate", an X.509 certificate with the
// DelegationUsage extension. The extension is defined in
// specified in https://tools.ietf.org/html/draft-ietf-tls-subcerts-02.
const DcCertWithDelegationUsage = `-----BEGIN CERTIFICATE-----
MIIBejCCASGgAwIBAgIQXXtl0v50W2OadoW0QwLUlzAKBggqhkjOPQQDAjAUMRIw
EAYDVQQKEwlBY21lIEluYy4wHhcNMTgwNzMwMjAxMTE5WhcNMTgwODA2MjAxMTE5
WjAUMRIwEAYDVQQKEwlBY21lIEluYy4wWTATBgcqhkjOPQIBBggqhkjOPQMBBwNC
AATcQuuaUNJ3kqKGs4DBdJVd7zWzyGANT4uBNGVkZ2cgaDsdFnx99fGibfgoWer8
HLt9Z+S6Hs+8bDPBHNgTR/Lfo1UwUzAOBgNVHQ8BAf8EBAMCB4AwEwYDVR0lBAww
CgYIKwYBBQUHAwEwDAYDVR0TAQH/BAIwADAPBgNVHREECDAGhwR/AAABMA0GCSsG
AQQBgtpLLAQAMAoGCCqGSM49BAMCA0cAMEQCIEMdIkwwmzQAJ6RSDT3wcrsySx2B
5Lvx5HGzc43Fgu9eAiAi4sFXnizFBVUL43qXZBq4ARw17o0JW3/7eec1xttQhw==
-----END CERTIFICATE-----
`

// The PEM-encoded "delegation key", the secret key associated with the
// delegation certificate. This is a key for ECDSA with P256 and SHA256.
const DcKeyWithDelegationUsage = `-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIAS/pGktmxK1hlt3gF4N2nkMrJnoZihvOO63nnNcxXQroAoGCCqGSM49
AwEHoUQDQgAE3ELrmlDSd5KihrOAwXSVXe81s8hgDU+LgTRlZGdnIGg7HRZ8ffXx
om34KFnq/By7fWfkuh7PvGwzwRzYE0fy3w==
-----END EC PRIVATE KEY-----
`

// A certificate without the DelegationUsage extension.
const DcCertWithoutDelegationUsage = `-----BEGIN CERTIFICATE-----
MIIBajCCAQ+gAwIBAgIRAMUg/VFqJaWWJwZ9iHoMjqIwCgYIKoZIzj0EAwIwEjEQ
MA4GA1UEChMHQWNtZSBDbzAeFw0xODA3MzAyMDExMTlaFw0xOTA3MzAyMDExMTla
MBIxEDAOBgNVBAoTB0FjbWUgQ28wWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATA
n+oeWSvSNHhEskSRgkkerCQDoV/NA+r3S5AtCOFT5AYLt8xltSTWerFI/YlZLIcL
xlJPT7T+XpBnfS6xaAuxo0YwRDAOBgNVHQ8BAf8EBAMCBaAwEwYDVR0lBAwwCgYI
KwYBBQUHAwEwDAYDVR0TAQH/BAIwADAPBgNVHREECDAGhwR/AAABMAoGCCqGSM49
BAMCA0kAMEYCIQCFGWnoJmwH1rxNCKBJWVDBKDTSsYhySRk4h9RPyR8bUwIhAJxc
KFyrowMTan791RJnyANH/4uYhmvkfhfrFGSTXUli
-----END CERTIFICATE-----
`

// The secret key associatted with DcCertWithoutDelegationUsage.
const DcKeyWithoutDelegationUsage = `-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIEP82pOhzx0tKkky9t0OmUo9MHgmfdAHxDN2cHmWGqOhoAoGCCqGSM49
AwEHoUQDQgAEwJ/qHlkr0jR4RLJEkYJJHqwkA6FfzQPq90uQLQjhU+QGC7fMZbUk
1nqxSP2JWSyHC8ZST0+0/l6QZ30usWgLsQ==
-----END EC PRIVATE KEY-----
`

// dcTestDC stores delegated credentials and their secret keys.
type dcTestDC struct {
Name string
Version int
Scheme int
DC []byte
PrivateKey []byte
}

// Use with maxVersion == VersionTLS13.
const DcTestDataTLS13PEM = `-----BEGIN DC TEST DATA-----
MIIIQzCCAUMTCXRsczEzcDI1NgICAwQCAgQDBIGyAAk6gAQDAwQAAFswWTATBgcq
hkjOPQIBBggqhkjOPQMBBwNCAAQpQtUm8AWOzCN+aGUVsoKH9lZWNqkQCBGhpVtT
u3ye6ACcwgNf81AYQ1ROb3EbWrnbvq9ap4a5QJ8AcrhZ9u0dBAMASDBGAiEA7LHb
Fh+RDi9RTRjlP0+b2eP+4CDtuK0qKSjf4kFbJ9ICIQDB/XIXkLV6qLW70MhFWCUi
2eqyhwtvTuMyATEJnyHKvwR5MHcCAQEEILHC94EWZnuVJqrbq3U+BnEU8BQPGfk6
pkB7mD8wqhl/oAoGCCqGSM49AwEHoUQDQgAEKULVJvAFjswjfmhlFbKCh/ZWVjap
EAgRoaVbU7t8nugAnMIDX/NQGENUTm9xG1q5276vWqeGuUCfAHK4WfbtHTCCAesT
CXRsczEzcDUyMQICAwQCAgYDBIHzAAk6gAYDAwQAAJ4wgZswEAYHKoZIzj0CAQYF
K4EEACMDgYYABAHgWg5NSn/t/BBxU9uWVBwIz3NWfq2xo1eQMsJY1ui9ILtmFsLn
QF1jbGrjlBZoh2sbHPFPl7yMOSYyVBFryhTaiQG7x11/Xs9fNC6AUm/6wROLMHTr
qCkiqCjIKVtBaM8FCAfPLoJHzPUu/h79Q0IdBlVhl4nEa4cWVW34cECfT+YdjgQD
AEYwRAIge+tF+cai/jfZtzUaVTcVuZfdIcGpRy4CfI2tKLipDCQCIAVigOh2jOFh
QWbX4h4Vz3ULoIuM+3wsFad0S0oH1v9HBIHfMIHcAgEBBEIAzNpPpiTsrv+0a3oA
CaGGr83/2Z632tygYjEOs919YrLR1Xe83hf5AvJLUz6u3RRlQdqwyPGQ1wm8baQ6
E0Pf6j+gBwYFK4EEACOhgYkDgYYABAHgWg5NSn/t/BBxU9uWVBwIz3NWfq2xo1eQ
MsJY1ui9ILtmFsLnQF1jbGrjlBZoh2sbHPFPl7yMOSYyVBFryhTaiQG7x11/Xs9f
NC6AUm/6wROLMHTrqCkiqCjIKVtBaM8FCAfPLoJHzPUu/h79Q0IdBlVhl4nEa4cW
VW34cECfT+YdjjCCAUITB2JhZHZlcnMCAwD/AAICBAMEgbIACTqABAP/AAAAWzBZ
MBMGByqGSM49AgEGCCqGSM49AwEHA0IABCPo5FSmarRgC/15bymE+3s4TXyQH9Oh
nlcKbAR70jqWLr9jbyjT7dy09sr5B6cVlw8AU2TeojdRUNG7y4nKnLsEAwBIMEYC
IQDZiMm7SoNMMvvrlxOF0OMSt1/hMOras702RDI2wvT92gIhAKgCmYucgBUIqMJd
d6g2FcY9UZnPzvnSuX9uBm38RMLMBHkwdwIBAQQgnx2Os1Z5kbZo61ItkpwJ0khL
7zgzLcc1X4unR3R56q+gCgYIKoZIzj0DAQehRANCAAQj6ORUpmq0YAv9eW8phPt7
OE18kB/ToZ5XCmwEe9I6li6/Y28o0+3ctPbK+QenFZcPAFNk3qI3UVDRu8uJypy7
MIIBPxMGYmFka2V5AgIDBAICBAMEgbEACTqABAMDBAAAWzBZMBMGByqGSM49AgEG
CCqGSM49AwEHA0IABGGXD4Td3D7im9y0S1wGoFgL4afAiklkSlQcNus2XfGUJS4c
io+gm4NBMcXby6LpN4lg5/0+K0i448WrIdd2eBYEAwBHMEUCIBMirxmjL9Yeigpl
aeqHncrT4V2u+sYBqa+dUUCXDTaqAiEAuR2geInXmNRtGWVltZh1pnohvwloPVvu
XK5qUb9g6/gEeTB3AgEBBCDk7f6Fto9m6vEDYiZapi2Hm8ranfS0AOgfnDfsRQa5
PKAKBggqhkjOPQMBB6FEA0IABFmA7YsXewnCF0R5eHLBwn4RsF1F5IwB8ZLpL2v4
GBD6YHmZDPBZ2/SZ3LxLGgT5yiO1/5y2ujDXsQ9X78ucHn8wggE+EwZiYWRzaWcC
AgMEAgIEAwSBsAAJOoAEAwMEAABbMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE
W2eqjqibupKlU/BwVWwfNE1qUdxqhF3cen0aKl8in24PcEi3AH1Y/zubsjoKah/q
YUfcmgAvhvsSFqohWzMa5gQDAEYwRAIgT4Tm7648J1OuTrn+HAJXVfzoXbcL/QUx
YxVDcpxytkoCIDulABj6w3EoQLoq8b1V781oPHKkUR7+L/SUPj/DxKQ2BHkwdwIB
AQQgIAwscB81XCsAujU+tr75y7yMFfSLtFkPAzn3/GiXpoWgCgYIKoZIzj0DAQeh
RANCAARbZ6qOqJu6kqVT8HBVbB80TWpR3GqEXdx6fRoqXyKfbg9wSLcAfVj/O5uy
OgpqH+phR9yaAC+G+xIWqiFbMxrmMIIBPhMFdGxzMTICAgMDAgIEAwSBsQAJOoAE
AwMDAABbMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEnsChIIFXdvdOTFnf3cyv
MsHKpSy68X+SbepvhPg+MBrn+ly9mb+hWPp2j0UJKiXwQmMf4vicNOYyjreml8Hf
VQQDAEcwRQIhANfDJ57MDLZqtye+uolguWx39vhfkvB9svEjYZwWTcoKAiALBgkH
OoRxcalH9qbE2p6LHLszqYyYW312aTHHYF0/6QR5MHcCAQEEILFX1gHwKwJwAQI+
GNisTdlwN0clslAccLogW0ON0gAZoAoGCCqGSM49AwEHoUQDQgAEnsChIIFXdvdO
TFnf3cyvMsHKpSy68X+SbepvhPg+MBrn+ly9mb+hWPp2j0UJKiXwQmMf4vicNOYy
jreml8HfVQ==
-----END DC TEST DATA-----`

// Parses the input PEM block containing the test DCs.
func dcLoadTestData(in []byte, out *[]dcTestDC) error {
block, _ := pem.Decode(in)
if block == nil {
return errors.New("failed to decode DC tests PEM block")
}

// Parse the DER-encoded test DCs.
_, err := asn1.Unmarshal(block.Bytes, out)
if err != nil {
return errors.New("failed to unmarshal DC test ASN.1 data")
}

// Check that the test data is for the right version. This should be
// maxVersion, defined in common.go.
for _, test := range *out {
dc, err := unmarshalDelegatedCredential(test.DC)
if err != nil {
return err
}

// Sanity check that test version matches the version encoded by the DC.
testVersion := uint16(test.Version)
if dc.cred.expectedVersion != testVersion {
return fmt.Errorf(
"test version doesn't match credential version: got: 0x0%04x; want: 0x%04x",
testVersion, dc.cred.expectedVersion)
}

// With the exception of "badvers" and "tsl12", all test DCs should have
// the expected verison.
if test.Name != "badvers" && test.Name != "tls12" && testVersion != maxVersion {
return fmt.Errorf(
"encountered test with wrong version: got: 0x0%04x; want: 0x%04x",
test.Version, maxVersion)
}
}
return nil
}

var dcTestDCs []dcTestDC
var dcTestConfig *Config
var dcTestDelegationCert Certificate
var dcTestCert Certificate
var dcTestNow time.Time

func init() {
// Load the DC test data.
var testData []byte
if maxVersion != 0x0304 {
panic(fmt.Errorf("no test data for version %04x", maxVersion))
}
testData = []byte(DcTestDataTLS13PEM)

err := dcLoadTestData(testData, &dcTestDCs)
if err != nil {
panic(err)
}

// The base configuration for the client and server.
dcTestConfig = &Config{
Time: func() time.Time {
return dcTestNow
},
Rand: zeroSource{},
Certificates: nil,
MinVersion: VersionTLS10,
MaxVersion: VersionTLS13,
CipherSuites: allCipherSuites(),
}

// The delegation certificate.
dcTestDelegationCert, err = X509KeyPair([]byte(DcCertWithDelegationUsage), []byte(DcKeyWithDelegationUsage))
if err != nil {
panic(err)
}
dcTestDelegationCert.Leaf, err = x509.ParseCertificate(dcTestDelegationCert.Certificate[0])
if err != nil {
panic(err)
}

// A certificate without the the DelegationUsage extension for X.509.
dcTestCert, err = X509KeyPair([]byte(DcCertWithoutDelegationUsage), []byte(DcKeyWithoutDelegationUsage))
if err != nil {
panic(err)
}
dcTestCert.Leaf, err = x509.ParseCertificate(dcTestCert.Certificate[0])
if err != nil {
panic(err)
}

// For testing purposes, use the point at which the test DCs were generated
// as the current time. This is the same as the time at which the
// delegation certificate was generated.
dcTestNow = dcTestDelegationCert.Leaf.NotBefore

// Make these roots of these certificates the client's trusted CAs.
dcTestConfig.RootCAs = x509.NewCertPool()

raw := dcTestDelegationCert.Certificate[len(dcTestDelegationCert.Certificate)-1]
root, err := x509.ParseCertificate(raw)
if err != nil {
panic(err)
}
dcTestConfig.RootCAs.AddCert(root)

raw = dcTestCert.Certificate[len(dcTestCert.Certificate)-1]
root, err = x509.ParseCertificate(raw)
if err != nil {
panic(err)
}
dcTestConfig.RootCAs.AddCert(root)
}

// Executes the handshake with the given configuration and returns true if the
// delegated credential extension was successfully negotiated.
func testConnWithDC(t *testing.T, clientConfig, serverConfig *Config) (bool, error) {
ln := newLocalListener(t)
defer ln.Close()

// Listen for and serve a single client connection.
srvCh := make(chan *Conn, 1)
var serr error
go func() {
sconn, err := ln.Accept()
if err != nil {
serr = err
srvCh <- nil
return
}
srv := Server(sconn, serverConfig)
if err := srv.Handshake(); err != nil {
serr = fmt.Errorf("handshake: %v", err)
srvCh <- nil
return
}
srvCh <- srv
}()

// Dial the server.
cli, err := Dial("tcp", ln.Addr().String(), clientConfig)
if err != nil {
return false, err
}
defer cli.Close()

srv := <-srvCh
if srv == nil {
return false, serr
}

// Return true if the client's conn.dc structure was instantiated.
st := cli.ConnectionState()
return (st.DelegatedCredential != nil), nil
}

// Checks that the client suppports a version >= 1.2 and accepts delegated
// credentials. If so, it returns the delegation certificate; otherwise it
// returns a plain certificate.
func testServerGetCertificate(ch *ClientHelloInfo) (*Certificate, error) {
versOk := false
for _, vers := range ch.SupportedVersions {
versOk = versOk || (vers >= uint16(VersionTLS12))
}

if versOk && ch.AcceptsDelegatedCredential {
return &dcTestDelegationCert, nil
}
return &dcTestCert, nil
}

// Various test cases for handshakes involving DCs.
var dcTesters = []struct {
clientDC bool
serverDC bool
clientSkipVerify bool
clientMaxVers uint16
serverMaxVers uint16
nowOffset time.Duration
dcTestName string
expectSuccess bool
expectDC bool
name string
}{
{true, true, false, VersionTLS13, VersionTLS13, 0, "tls13p256", true, true, "tls13"},
{true, true, false, VersionTLS13, VersionTLS13, 0, "tls13p521", true, true, "tls13"},
{true, false, false, VersionTLS13, VersionTLS13, 0, "tls13p256", true, false, "server no dc"},
{true, true, false, VersionTLS12, VersionTLS13, 0, "tls13p256", true, false, "client old"},
{true, true, false, VersionTLS13, VersionTLS12, 0, "tls13p256", true, false, "server old"},
{true, true, false, VersionTLS13, VersionTLS13, 0, "badkey", false, false, "bad key"},
{true, true, true, VersionTLS13, VersionTLS13, 0, "badsig", true, true, "bad key, skip verify"},
{true, true, false, VersionTLS13, VersionTLS13, dcMaxTTL, "tls13", false, false, "expired dc"},
{true, true, false, VersionTLS13, VersionTLS13, 0, "badvers", false, false, "dc wrong version"},
{true, true, false, VersionTLS12, VersionTLS12, 0, "tls12", true, false, "tls12"},
}

// Tests the handshake with the delegated credential extension for each test
// case in dcTests.
func TestDCHandshake(t *testing.T) {
clientConfig := dcTestConfig.Clone()
serverConfig := dcTestConfig.Clone()
serverConfig.GetCertificate = testServerGetCertificate

for i, tester := range dcTesters {
clientConfig.MaxVersion = tester.clientMaxVers
serverConfig.MaxVersion = tester.serverMaxVers
clientConfig.InsecureSkipVerify = tester.clientSkipVerify
clientConfig.AcceptDelegatedCredential = tester.clientDC
clientConfig.Time = func() time.Time {
return dcTestNow.Add(time.Duration(tester.nowOffset))
}

if tester.serverDC {
serverConfig.GetDelegatedCredential = func(
ch *ClientHelloInfo, vers uint16) ([]byte, crypto.PrivateKey, error) {
if vers < VersionTLS13 {
return nil, nil, nil
}
for _, test := range dcTestDCs {
if test.Name == tester.dcTestName {
sk, err := x509.ParseECPrivateKey(test.PrivateKey)
if err != nil {
return nil, nil, err
}
return test.DC, sk, nil
}
}
return nil, nil, fmt.Errorf("Test DC with name '%s' not found", tester.dcTestName)
}
} else {
serverConfig.GetDelegatedCredential = nil
}

usedDC, err := testConnWithDC(t, clientConfig, serverConfig)
if err != nil && tester.expectSuccess {
t.Errorf("test #%d (%s) fails: %s", i+1, tester.name, err)
} else if err == nil && !tester.expectSuccess {
t.Errorf("test #%d (%s) succeeds; expected failure", i+1, tester.name)
}

if usedDC != tester.expectDC {
t.Errorf("test #%d (%s) usedDC = %v; expected %v", i+1, tester.name, usedDC, tester.expectDC)
}
}
}

+ 11
- 0
testdata/example-cert.pem View File

@@ -0,0 +1,11 @@
-----BEGIN CERTIFICATE-----
MIIBhTCCASugAwIBAgIQIRi6zePL6mKjOipn+dNuaTAKBggqhkjOPQQDAjASMRAw
DgYDVQQKEwdBY21lIENvMB4XDTE3MTAyMDE5NDMwNloXDTE4MTAyMDE5NDMwNlow
EjEQMA4GA1UEChMHQWNtZSBDbzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABD0d
7VNhbWvZLWPuj/RtHFjvtJBEwOkhbN/BnnE8rnZR8+sbwnc/KhCk3FhnpHZnQz7B
5aETbbIgmuvewdjvSBSjYzBhMA4GA1UdDwEB/wQEAwICpDATBgNVHSUEDDAKBggr
BgEFBQcDATAPBgNVHRMBAf8EBTADAQH/MCkGA1UdEQQiMCCCDmxvY2FsaG9zdDo1
NDUzgg4xMjcuMC4wLjE6NTQ1MzAKBggqhkjOPQQDAgNIADBFAiEA2zpJEPQyz6/l
Wf86aX6PepsntZv2GYlA5UpabfT2EZICICpJ5h/iI+i341gBmLiAFQOyTDT+/wQc
6MF9+Yw1Yy0t
-----END CERTIFICATE-----

+ 5
- 0
testdata/example-key.pem View File

@@ -0,0 +1,5 @@
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIIrYSSNQFaA2Hwf1duRSxKtLYX5CB04fSeQ6tF1aY/PuoAoGCCqGSM49
AwEHoUQDQgAEPR3tU2Fta9ktY+6P9G0cWO+0kETA6SFs38GecTyudlHz6xvCdz8q
EKTcWGekdmdDPsHloRNtsiCa697B2O9IFA==
-----END EC PRIVATE KEY-----

+ 10
- 2
ticket.go View File

@@ -37,6 +37,7 @@ type SessionTicketSealer interface {
type sessionState struct { type sessionState struct {
vers uint16 vers uint16
cipherSuite uint16 cipherSuite uint16
usedEMS bool
masterSecret []byte masterSecret []byte
certificates [][]byte certificates [][]byte
// usedOldKey is true if the ticket from which this session came from // usedOldKey is true if the ticket from which this session came from
@@ -51,6 +52,7 @@ func (s *sessionState) equal(i interface{}) bool {
} }


if s.vers != s1.vers || if s.vers != s1.vers ||
s.usedEMS != s1.usedEMS ||
s.cipherSuite != s1.cipherSuite || s.cipherSuite != s1.cipherSuite ||
!bytes.Equal(s.masterSecret, s1.masterSecret) { !bytes.Equal(s.masterSecret, s1.masterSecret) {
return false return false
@@ -77,7 +79,12 @@ func (s *sessionState) marshal() []byte {


ret := make([]byte, length) ret := make([]byte, length)
x := ret x := ret
x[0] = byte(s.vers >> 8)
was_used := byte(0)
if s.usedEMS {
was_used = byte(0x80)
}

x[0] = byte(s.vers>>8) | byte(was_used)
x[1] = byte(s.vers) x[1] = byte(s.vers)
x[2] = byte(s.cipherSuite >> 8) x[2] = byte(s.cipherSuite >> 8)
x[3] = byte(s.cipherSuite) x[3] = byte(s.cipherSuite)
@@ -108,8 +115,9 @@ func (s *sessionState) unmarshal(data []byte) alert {
return alertDecodeError return alertDecodeError
} }


s.vers = uint16(data[0])<<8 | uint16(data[1])
s.vers = (uint16(data[0])<<8 | uint16(data[1])) & 0x7fff
s.cipherSuite = uint16(data[2])<<8 | uint16(data[3]) s.cipherSuite = uint16(data[2])<<8 | uint16(data[3])
s.usedEMS = (data[0] & 0x80) == 0x80
masterSecretLen := int(data[4])<<8 | int(data[5]) masterSecretLen := int(data[4])<<8 | int(data[5])
data = data[6:] data = data[6:]
if len(data) < masterSecretLen { if len(data) < masterSecretLen {


+ 4
- 5
tls.go View File

@@ -237,15 +237,14 @@ func X509KeyPair(certPEMBlock, keyPEMBlock []byte) (Certificate, error) {
skippedBlockTypes = append(skippedBlockTypes, keyDERBlock.Type) skippedBlockTypes = append(skippedBlockTypes, keyDERBlock.Type)
} }


var err error
cert.PrivateKey, err = parsePrivateKey(keyDERBlock.Bytes)
// We don't need to parse the public key for TLS, but we so do anyway
// to check that it looks sane and matches the private key.
x509Cert, err := x509.ParseCertificate(cert.Certificate[0])
if err != nil { if err != nil {
return fail(err) return fail(err)
} }


// We don't need to parse the public key for TLS, but we so do anyway
// to check that it looks sane and matches the private key.
x509Cert, err := x509.ParseCertificate(cert.Certificate[0])
cert.PrivateKey, err = parsePrivateKey(keyDERBlock.Bytes)
if err != nil { if err != nil {
return fail(err) return fail(err)
} }


+ 5
- 1
tls_test.go View File

@@ -674,7 +674,7 @@ func TestCloneNonFuncFields(t *testing.T) {
switch fn := typ.Field(i).Name; fn { switch fn := typ.Field(i).Name; fn {
case "Rand": case "Rand":
f.Set(reflect.ValueOf(io.Reader(os.Stdin))) f.Set(reflect.ValueOf(io.Reader(os.Stdin)))
case "Time", "GetCertificate", "GetConfigForClient", "VerifyPeerCertificate", "GetClientCertificate":
case "Time", "GetCertificate", "GetConfigForClient", "VerifyPeerCertificate", "GetClientCertificate", "GetDelegatedCredential":
// DeepEqual can't compare functions. If you add a // DeepEqual can't compare functions. If you add a
// function field to this list, you must also change // function field to this list, you must also change
// TestCloneFuncFields to ensure that the func field is // TestCloneFuncFields to ensure that the func field is
@@ -713,6 +713,10 @@ func TestCloneNonFuncFields(t *testing.T) {
f.Set(reflect.ValueOf(uint32(0))) f.Set(reflect.ValueOf(uint32(0)))
case "SessionTicketSealer": case "SessionTicketSealer":
// TODO // TODO
case "AcceptDelegatedCredential":
f.Set(reflect.ValueOf(false))
case "UseExtendedMasterSecret":
f.Set(reflect.ValueOf(false))
default: default:
t.Errorf("all fields must be accounted for, but saw unknown field %q", fn) t.Errorf("all fields must be accounted for, but saw unknown field %q", fn)
} }


Loading…
Cancel
Save