Upphovsman | SHA1 | Meddelande | Datum |
---|---|---|---|
Kris Kwiatkowski | aca40d198f | This is implementation of server which is running on crypto.cloudflare.com | 5 år sedan |
Kris Kwiatkowski | c362368c5b | Fixes log message | 5 år sedan |
Henry Case |
600d4dece5
|
Adds X25519-SIKEp503 key agreement (#159)
* removes useless variable * [sike] Adds X25519-SIKEp503 with cSHAKE |
5 år sedan |
Henry Case |
a5d35123cc
|
[sike] Refactor key agreement in TLS 1.3 [PATCH 1/2] (#153)
Previously there where two methods used for key agreemnt tls.Conn::generateKeyShare and tls.Conn::deriveDHESecret. Both were used on client and server side. Boolean flag is used in order to differentiate between key agreement performed on client and on server side. Which sucks badly. In order to implement shared secret agreement with KEM it is better to add method which implements server specific key agreement and provide default implementation which reuses tls.Conn::generateKeyShare followed by tls.Conn::deriveDHESecret. Now, it is possible for most of the DH-style key agreements to reuse default implementation and for KEM-style key agreement to provide server specific implementation. |
5 år sedan |
Henry Case |
7619b84b13
|
Fixes GH#154. Updates client certificates (#155) | 5 år sedan |
Kris Kwiatkowski | 76231e7564 |
SIDH: Implement test againt TLSv1.2
Tris tries to connect to BoringSSL over TLS 1.2 with X25519-SIDH as prefered DH group. As this is not supported by BoringSSL it must fall back to P-256 (second preference on the list) Also refactors tris test client |
5 år sedan |
Kris Kwiatkowski | 334eee5310 | SIDH: Align codepoints with the ones in latest version | 5 år sedan |
Kris Kwiatkowski | 1c0d342ecc | boringssl: update to newest version | 5 år sedan |
Kris Kwiatkowski | 36f2800cb3 | adds LICENSE | 6 år sedan |
Kris Kwiatkowski | fab2c445b3 | makefile: use named tags instead of commit numbers | 6 år sedan |
Kris Kwiatkowski | c752e2e7a4 | test: adds tests for rsa-pss signatures | 6 år sedan |
Filippo Valsorda | d20745552c |
crypto/tls: expand docs on client-side session ticket support
Users are sometimes confused why session tickets are not enabled even if SessionTicketsDisabled is false. Change-Id: I3b783d2cf3eed693a3ad6acb40a8003db7e0b648 Reviewed-on: https://go-review.googlesource.com/117255 Reviewed-by: Adam Langley <agl@golang.org> |
6 år sedan |
Kris Kwiatkowski | 18024f59de |
crypto/tls: add examples for [Load]X509KeyPair
I was confused about how to start an HTTP server if the server cert/key are in memory, not on disk. I thought it would be good to show an example of how to use these two functions to accomplish that. example-cert.pem and example-key.pem were generated using crypto/tls/generate_cert.go. |
6 år sedan |
Filippo Valsorda | 4c970a6672 |
crypto/tls: parse certificate first in X509KeyPair to get better errors
parsePrivateKey can't return useful error messages because it does trial decoding of multiple formats. Try ParseCertificate first in case it offers a useful error message. Fixes #23591 Change-Id: I380490a5850bee593a7d2f584a27b2a14153d768 Reviewed-on: https://go-review.googlesource.com/90435 Run-TryBot: Filippo Valsorda <filippo@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Adam Langley <agl@golang.org> |
6 år sedan |
Kevin Burke | 9fc345bd63 |
crypto/tls: handle errors in generate_cert.go
I don't expect these to hit often, but we should still alert users if we fail to write the correct data to the file, or fail to close it. Change-Id: I33774e94108f7f18ed655ade8cca229b1993d4d2 Reviewed-on: https://go-review.googlesource.com/91456 Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org> |
6 år sedan |
Kris Kwiatkowski | 86b0aecf5e |
go 1.11: Switch to GO 1.11
Minimal amount of changes needed to switch to GO 1.11 * crypto/internal/cipherhw was removed and internal/cpu was introduced * wrong code formatting in handhsake_server_test.go was breaking the build |
6 år sedan |
Kris Kwiatkowski | 8da51abeca |
sidh: operability tests with BoringSSL
Implements two tests for SIDH/P503-X25519 interoperability. BoringSSL initiates connection to TRIS and TRIS initiates connection to BoringSSL. SIDH server always listens on port 7443 |
6 år sedan |
Kris Kwiatkowski | 96db6f14d8 | sidh: sidh support in boringssl | 6 år sedan |
Henry Case |
cb67de3e1f
|
sidh: use version with improved vendoring (#144) | 6 år sedan |
Kris Kwiatkowski | bcc2724bf6 | sidh: use implementation with improved performance for ARMv8 | 6 år sedan |
Marten Seemann | 6fcf1bc4c0 | don't expect CertificateVerify when the client doesn't send any cert | 6 år sedan |
Kris Kwiatkowski | ce53b126bc | fix: client shouldn't use P751/x448 ID | 6 år sedan |
Kris Kwiatkowski | 3895b624f3 | sidh: comment out P751/x448 | 6 år sedan |
Kris Kwiatkowski | d184bc0099 |
sidh: adds PQ secure KEX
* SIDH/P503-X25519 * adds interop tests |
6 år sedan |
Kris Kwiatkowski | 7c79cbefc5 | sidh: API | 6 år sedan |
Kris Kwiatkowski | ed36ba540a | sidh: updates for Makefile | 6 år sedan |
Kris Kwiatkowski | 61bc4c1a09 |
moves methods from Config to Conn receives
generateKeyShare has nothing to do with Config receiver. It doesn't configure anything. It has everything with connection so moved to Conn. Making deriveECDHESecret also a method of Conn will make it possible to access 'isClient' field. |
6 år sedan |
Kris Kwiatkowski | 07ad1769c3 |
fix: in TLSv1.3, the ALPN extension must be sent in EE
serverHandshakeState::readClientHello was setting selected ALPN protocol always on hs.hello.alpnProtocol, which is specific to TLS 1.2 and older. Because of that server was marshalling ALPN to SH instead of EE. |
6 år sedan |
Kris Kwiatkowski | da110326f8 | Swap TLS 1.3 to RFC 8446 | 6 år sedan |
Watson Ladd | 7e1760cc7c |
Add EMS support to tls-tris
see RFC7627 |
7 år sedan |
Kris Kwiatkowski | 58c559ba00 |
fix: mac calculation fails when subsequent packet is shorter
Before AEAD encryption data are stored in a buffer. Last possition of this buffer store data type. When subsequent TLS record is shorter than previous, the buffer is shrinked. This causes to remove data type, which results in wrong MAC calculation. Only in case of TLS 1.3. |
6 år sedan |
Kris Kwiatkowski | 1678dc5074 | cleanup: removes Committer interface | 6 år sedan |
Marten Seemann | e20b4d2a9a |
don't generate a key share with Curve ID 0 in the ServerHello tests
0 is used as a sentinel value when determining if the key share should be marshalled. |
6 år sedan |
Marten Seemann | 9919e0e977 |
fix the key_exchange length in the ClientHello and ServerHello tests
The marshaller correctly checks the key_exchange length, but the test code sometimes generated empty key_exchanges. |
6 år sedan |
Marten Seemann | e7a33a2dc3 | cherry-pick https://go-review.googlesource.com/c/go/+/129755 | 6 år sedan |
Henry Case |
81871bbad5
|
tls tris server: allow custom server keypairs (#128) | 6 år sedan |
Henry Case |
2bcf6466b4
|
removes old draft version indicators (#127) | 6 år sedan |
Marten Seemann | 40eb693877 | remove support for generating draft 18-21 ServerHellos (#124) | 6 år sedan |
Kris Kwiatkowski | 242e89da1b | refactoring of the tris test client | 6 år sedan |
Kris Kwiatkowski | 63ec8fff02 | refactoring of the tris test server | 6 år sedan |
Henry Case |
a21fd9c1bc
|
refactors record encryption code (#122) | 6 år sedan |
Henry D. Case | e81269b57e |
Revert "Small refactoring of record encryption code"
This reverts commit
|
6 år sedan |
Henry D. Case | 1782162852 | Small refactoring of record encryption code | 6 år sedan |
Henry D. Case | ad86d61c42 | Let's use constant instead of hardcoding values (same is done in bssl) | 6 år sedan |
Henry D. Case | 91a6fcebab | Cleanup | 6 år sedan |
Christopher Patton | a2fe2d9a71 |
DC draft-02, last minute change (#121)
* Drop DC test data for draft23 * DC: Change public key type There was a last minute change to spec that changes the public key type from a byte string of length at most 2^16 to a byte string of length at most 2^24. |
6 år sedan |
Henry Case |
e77e39e7aa
|
server must NOT send version prior to TLS1.3 in supported_versions (#119)
As per 4.2.1, client must abort with illegal_parameter in case it gets version 0x0303 or older in supported_versions extensions |
6 år sedan |
Henry Case |
d3e18f99e2
|
Minimal number of changes needed to udpate to draft-28 (#115)
* includes AD in authentication check of TLS records As per 5.2 of TLS 1.3 draft-28, the additional data is record header. * tests: Update tests in order to support draft-28 * Interoperability: Updates NSS and BoringSSL versions to the one supporting draft-28 * Bogo: Updates revision number to use tests for draft-28 * FIX: makefile was using test-compat target instead of test-interop * DC test: constify * Use binary interface to encode in big-endian |
6 år sedan |
Christopher Patton | 0d6e4561a6 |
Add DC test data for tls13draft28 and tls13rfc (#117)
The test in subcerts_test.go only passes if maxVersion == VersionTLS13Draft23. This is because DCs are cryptographically bound to the protocol version on the wire. To work around this as we move towards the RFC, this PR adds test data for VersionTLS13Draft28 and VersionTLS13 and uses maxVersion to pick which data to load. # Please enter the commit message for your changes. Lines starting |
6 år sedan |
Christopher Patton | 174a68a0fb |
Update implementation of draft-ietf-tls-subcerts to draft 02 (#108)
Drops support for delegated credentials with TLS 1.2 and adds the protocol version and signature algorithm to the credential structure. |
6 år sedan |
Henry D. Case | 77d1fbf262 | Don't use VersionTLS13DraftXX anywhere | 6 år sedan |
Christopher Patton | c5280001a4 |
Remove delegated credential minting from the API
What's left are the minimal API changes required to use the delegated credential extension in the TLS handshake. |
6 år sedan |
Christopher Patton | 1ea9624098 |
Use static test data for testing delegated credentials
This removes dependency on NewDelegatedCredential from tris. |
6 år sedan |
Brendan Mc |
22d6deb0e7
|
Merge pull request #95 from cjpatton/subcerts
Delegated credentials for TLS |
6 år sedan |
Christopher Patton | 84fe9084cd |
Implement the delegated_credential extension for TLS
This complies with the standard, currently an Internet draft: https://tlswg.github.io/tls-subcerts/draft-ietf-tls-subcerts.html It also adds a minimal interface for generating new delegated credentials. |
6 år sedan |
Christopher Patton | 963d5877be |
Refactor the keyAgreement interface
It's sufficient to pass in the *tls.Certificate (resp. *x509.Certificate) to the server functions (resp. client funcctions), but not necessary; the existing keyAgreement implementations only makes use of the private key (resp. public key). Moreover, this change is necessary for implementing the delegated credentials extension, which replaces the private key (resp. public key) used in the handshake. |
6 år sedan |
Henry D. Case | 3ff71dcdc5 | tests: enable client authentication in bogo | 6 år sedan |
Henry D. Case | 6e4abe2d07 |
TLSv1.3 draft-23: align tests
* Changes tests so that they pass with draft-23 * BoringSSL interoperability: uses code at most recent commit. It uses "-tls13-variant draft23" flag to indicate compatibility with draft23 * NSS interoperability: Uses release 3.35 * PicoTLS interoperability: blocked. Doesn't seem to implement draft23 * Uses updated bogo from https://github.com/henrydcase/crypto-tls-bogo-shim |
6 år sedan |
Henry D. Case | 03138ec18e |
TLSv1.3 -draft23: Implementation of signature_algorithms_cert
Tris uses signature_algorithms_cert in order to advertise that it doesn't support RSA-PSS. See GH#86 for more detailed discussion. |
6 år sedan |
Henry D. Case | 5bdf1af124 | TLS1.3 -draft23: Renumber key_share | 6 år sedan |
Evan Klitzke | 67bc308e04 | Update client SCT list during TLS 1.3 handshake, fixes #76 | 6 år sedan |
Henry D. Case | b1d6c0aeaa | Change function name verifyPeerCertificate->verifyPeerHandshakeSignature | 6 år sedan |
Henry D. Case | 91d6db578b | CI: Fail build if code is wrongly formatted | 6 år sedan |
Henry D. Case | b6af4dd343 | Code formatting for handshake_server_test.go | 6 år sedan |
Henry D. Case | 3f720fc50c | Code formatting for TRIS test client and server | 6 år sedan |
@@ -5,7 +5,7 @@ services: | |||
- docker | |||
go: | |||
- 1.10.x | |||
- 1.11.x | |||
env: | |||
- TEST_SUITE=test-unit | |||
@@ -15,6 +15,9 @@ env: | |||
matrix: | |||
fast_finish: true | |||
before_install: | |||
- make -f _dev/Makefile fmtcheck | |||
install: | |||
- sudo pip install docker | |||
@@ -21,6 +21,8 @@ import ( | |||
"sync/atomic" | |||
"time" | |||
sidh "github_com/cloudflare/sidh/sidh" | |||
sike "github_com/cloudflare/sidh/sike" | |||
"golang_org/x/crypto/curve25519" | |||
) | |||
@@ -30,6 +32,25 @@ const numSessionTickets = 2 | |||
type secretLabel int | |||
const ( | |||
// Both public key and shared secret size | |||
x25519Sz = 32 | |||
SIDHp503PubKeySz = 378 | |||
SIDHp503PrvKeySz = 32 | |||
SIDHp503SharedSecretSz = 126 | |||
SIDHp503Curve25519PubKeySz = x25519Sz + SIDHp503PubKeySz | |||
SIDHp503Curve25519PrvKeySz = x25519Sz + SIDHp503PrvKeySz | |||
SIDHp503Curve25519SharedKeySz = x25519Sz + SIDHp503SharedSecretSz | |||
SIKEp503SharedSecretSz = 16 | |||
SIKEp503CtSz = SIDHp503PubKeySz + 24 | |||
SIKEp503Curve25519CtSz = x25519Sz + SIKEp503CtSz | |||
SIKEp503Curve25519PubKeySz = x25519Sz + SIDHp503PubKeySz | |||
SIKEp503Curve25519PrvKeySz = x25519Sz + SIDHp503PrvKeySz + 24 | |||
SIKEp503Curve25519SharedKeySz = x25519Sz + SIKEp503SharedSecretSz | |||
) | |||
const ( | |||
secretResumptionPskBinder secretLabel = iota | |||
secretEarlyClient | |||
@@ -49,6 +70,78 @@ type keySchedule13 struct { | |||
config *Config // Used for KeyLogWriter callback, nil if keylogging is disabled. | |||
} | |||
// Interface implemented by key exchange strategies | |||
type kex interface { | |||
// c - context of current TLS handshake, groupId - ID of an algorithm | |||
// (curve/field) being chosen for key agreement. Methods implmenting an | |||
// interface always assume that provided groupId is correct. | |||
// | |||
// In case of success, function returns secret key and ephemeral key. Otherwise | |||
// error is set. | |||
generate(c *Conn, groupId CurveID) ([]byte, keyShare, error) | |||
// keyAgreementClient declares an API for implementing shared secret agreement on | |||
// the client side. `c` is a context of current TLS handshake, `ks` is a public key | |||
// received from the server, ``privateKey`` client private key. | |||
// Function returns shared secret in case of success or non nil error otherwise. | |||
keyAgreementClient(c *Conn, ks keyShare, privateKey []byte) ([]byte, error) | |||
// keyAgreementServer declares an API for implementing shared secret agreement on | |||
// the server side. `c` context of current TLS handshake, `ks` is a public key | |||
// received from the client side of the connection, ``privateKey`` is a private key | |||
// of a server. | |||
// Function returns secret shared between parties and public value to exchange | |||
// between parties. In case of failure `error` must be set. | |||
keyAgreementServer(c *Conn, ks keyShare) ([]byte, keyShare, error) | |||
} | |||
// defaultServerKEX is an abstract class defining default, common behaviour on | |||
// a server side. | |||
type defaultServerKEX struct{} | |||
// defaultServerKEX is an abstract class defining default implementation of | |||
// server side key agreement. It generates ephemeral key and uses it together | |||
// with client public part in order to calculate shared secret. | |||
func (defaultServerKEX) keyAgreementServer(c *Conn, clientKS keyShare) ([]byte, keyShare, error) { | |||
privateKey, publicKey, err := c.generateKeyShare(clientKS.group) | |||
if err != nil { | |||
c.sendAlert(alertInternalError) | |||
return nil, keyShare{}, err | |||
} | |||
// Use same key agreement implementation as on the client side | |||
ss, err := c.keyAgreementClient(clientKS, privateKey) | |||
if err != nil { | |||
c.sendAlert(alertIllegalParameter) | |||
return nil, keyShare{}, err | |||
} | |||
return ss, publicKey, nil | |||
} | |||
// Key Exchange strategies per curve type | |||
type kexNIST struct{ defaultServerKEX } // Used by NIST curves; P-256, P-384, P-512 | |||
type kexX25519 struct{ defaultServerKEX } // Used by X25519 | |||
type kexSIDHp503 struct{ defaultServerKEX } // Used by SIDH/P503 | |||
type kexSIKEp503 struct{} // Used by SIKE/P503 | |||
type kexHybridSIDHp503X25519 struct { | |||
defaultServerKEX | |||
classicKEX kexX25519 | |||
pqKEX kexSIDHp503 | |||
} // Used by SIDH-ECDH hybrid scheme | |||
type kexHybridSIKEp503X25519 struct { | |||
classicKEX kexX25519 | |||
pqKEX kexSIKEp503 | |||
} // Used by SIKE-ECDHE hybrid scheme | |||
// Routing map for key exchange strategies | |||
var kexStrat = map[CurveID]kex{ | |||
CurveP256: &kexNIST{}, | |||
CurveP384: &kexNIST{}, | |||
CurveP521: &kexNIST{}, | |||
X25519: &kexX25519{}, | |||
HybridSIDHp503Curve25519: &kexHybridSIDHp503X25519{}, | |||
HybridSIKEp503Curve25519: &kexHybridSIKEp503X25519{}, | |||
} | |||
func newKeySchedule13(suite *cipherSuite, config *Config, clientRandom []byte) *keySchedule13 { | |||
if config.KeyLogWriter == nil { | |||
clientRandom = nil | |||
@@ -74,6 +167,15 @@ func (ks *keySchedule13) setSecret(secret []byte) { | |||
ks.secret = hkdfExtract(hash, secret, salt) | |||
} | |||
// Depending on role returns pair of key variant to be used by | |||
// local and remote process. | |||
func getSidhKeyVariant(isClient bool) (sidh.KeyVariant, sidh.KeyVariant) { | |||
if isClient { | |||
return sidh.KeyVariant_SIDH_A, sidh.KeyVariant_SIDH_B | |||
} | |||
return sidh.KeyVariant_SIDH_B, sidh.KeyVariant_SIDH_A | |||
} | |||
// write appends the data to the transcript hash context. | |||
func (ks *keySchedule13) write(data []byte) { | |||
ks.handshakeCtx = nil | |||
@@ -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) | |||
hashSize := hash.Size() | |||
@@ -183,14 +268,17 @@ CurvePreferenceLoop: | |||
} | |||
hs.keySchedule.write(hs.clientHello.marshal()) | |||
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()) | |||
if _, err := c.writeRecord(recordTypeHandshake, hs.hello.marshal()); err != nil { | |||
@@ -202,7 +290,7 @@ CurvePreferenceLoop: | |||
return err | |||
} | |||
hs.keySchedule.setSecret(ecdheSecret) | |||
hs.keySchedule.setSecret(sharedSecret) | |||
clientCipher, cTrafficSecret := hs.keySchedule.prepareCipher(secretHandshakeClient) | |||
hs.hsClientCipher = clientCipher | |||
serverCipher, sTrafficSecret := hs.keySchedule.prepareCipher(secretHandshakeServer) | |||
@@ -225,6 +313,7 @@ CurvePreferenceLoop: | |||
certReq := new(certificateRequestMsg13) | |||
// extension 'signature_algorithms' MUST be specified | |||
certReq.supportedSignatureAlgorithms = supportedSignatureAlgorithms13 | |||
certReq.supportedSignatureAlgorithmsCert = supportedSigAlgorithmsCert(supportedSignatureAlgorithms13) | |||
hs.keySchedule.write(certReq.marshal()) | |||
if _, err := hs.c.writeRecord(recordTypeHandshake, certReq.marshal()); err != nil { | |||
return err | |||
@@ -301,11 +390,11 @@ func (hs *serverHandshakeState) readClientFinished13(hasConfirmLock bool) error | |||
} | |||
// 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) | |||
} | |||
@@ -316,39 +405,37 @@ func (hs *serverHandshakeState) readClientFinished13(hasConfirmLock bool) error | |||
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 | |||
msg, err = c.readHandshake() | |||
if err != nil { | |||
return err | |||
} | |||
} else if (c.config.ClientAuth >= RequestClientCert) && !c.didResume { | |||
c.sendAlert(alertCertificateRequired) | |||
return unexpectedMessageError(certMsg, msg) | |||
} | |||
clientFinished, ok := msg.(*finishedMsg) | |||
@@ -402,6 +489,16 @@ func (hs *serverHandshakeState) sendCertificate13() error { | |||
if len(certEntries) > 0 && hs.clientHello.scts { | |||
certEntries[0].sctList = hs.cert.SignedCertificateTimestamps | |||
} | |||
// If hs.delegatedCredential is set (see hs.readClientHello()) then the | |||
// server is using the delegated credential extension. The DC is added as an | |||
// extension to the end-entity certificate, i.e., the last CertificateEntry | |||
// of Certificate.certficate_list. (For details, see | |||
// https://tools.ietf.org/html/draft-ietf-tls-subcerts-02.) | |||
if len(certEntries) > 0 && hs.clientHello.delegatedCredential && hs.delegatedCredential != nil { | |||
certEntries[0].delegatedCredential = hs.delegatedCredential | |||
} | |||
certMsg := &certificateMsg13{certificates: certEntries} | |||
hs.keySchedule.write(certMsg.marshal()) | |||
@@ -422,7 +519,7 @@ func (hs *serverHandshakeState) sendCertificate13() error { | |||
} | |||
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 { | |||
c.sendAlert(alertInternalError) | |||
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 | |||
func (hs *serverHandshakeState) selectTLS13SignatureScheme() (sigScheme SignatureScheme, err error) { | |||
var supportedSchemes []SignatureScheme | |||
signer, ok := hs.cert.PrivateKey.(crypto.Signer) | |||
signer, ok := hs.privateKey.(crypto.Signer) | |||
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() | |||
if _, ok := pk.(*rsa.PublicKey); ok { | |||
@@ -540,61 +637,33 @@ func prepareDigitallySigned(hash crypto.Hash, context string, data []byte) []byt | |||
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 { | |||
@@ -841,7 +910,7 @@ func (hs *clientHandshakeState) processEncryptedExtensions(ee *encryptedExtensio | |||
return nil | |||
} | |||
func verifyPeerCertificate( | |||
func verifyPeerHandshakeSignature( | |||
certVerify *certificateVerifyMsg, | |||
pubKey crypto.PublicKey, | |||
signAlgosKnown []SignatureScheme, | |||
@@ -953,6 +1022,7 @@ func (hs *clientHandshakeState) doTLS13Handshake() error { | |||
hash := hashForSuite(hs.suite) | |||
hashSize := hash.Size() | |||
serverHello := hs.serverHello | |||
c.scts = serverHello.scts | |||
// middlebox compatibility mode, send CCS before second flight. | |||
if _, err := c.writeRecord(recordTypeChangeCipherSpec, []byte{1}); err != nil { | |||
@@ -969,14 +1039,14 @@ func (hs *clientHandshakeState) doTLS13Handshake() error { | |||
// 0-RTT is not supported yet, so use an empty PSK. | |||
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) | |||
return errors.New("tls: bad ECDHE server share") | |||
return err | |||
} | |||
// Calculate handshake secrets. | |||
hs.keySchedule.setSecret(ecdheSecret) | |||
hs.keySchedule.setSecret(sharedSecret) | |||
clientCipher, clientHandshakeSecret := hs.keySchedule.prepareCipher(secretHandshakeClient) | |||
serverCipher, serverHandshakeSecret := hs.keySchedule.prepareCipher(secretHandshakeServer) | |||
if c.hand.Len() > 0 { | |||
@@ -1032,6 +1102,7 @@ func (hs *clientHandshakeState) doTLS13Handshake() error { | |||
return unexpectedMessageError(certMsg, msg) | |||
} | |||
hs.keySchedule.write(certMsg.marshal()) | |||
// Validate certificates. | |||
certs := getCertsFromEntries(certMsg.certificates) | |||
if err := hs.processCertsFromServer(certs); err != nil { | |||
@@ -1048,9 +1119,32 @@ func (hs *clientHandshakeState) doTLS13Handshake() error { | |||
c.sendAlert(alertUnexpectedMessage) | |||
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, | |||
hs.c.peerCertificates[0].PublicKey, | |||
pk, | |||
hs.hello.supportedSignatureAlgorithms, | |||
hs.keySchedule.transcriptHash.Sum(nil), | |||
"TLS 1.3, server CertificateVerify") | |||
@@ -1113,3 +1207,295 @@ func (hs *clientHandshakeState) doTLS13Handshake() error { | |||
c.in.setCipher(c.vers, appServerCipher) | |||
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 | |||
} |
@@ -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. |
@@ -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 | |||
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 | |||
@@ -70,7 +71,7 @@ We run 3 kinds of test:. | |||
* Unit testing: <br/>``make -f _dev/Makefile test-unit`` | |||
* 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: | |||
``` | |||
@@ -8,24 +8,33 @@ BUILD_DIR ?= $(PRJ_DIR)/_dev/GOROOT | |||
# Compiler | |||
GO ?= go | |||
DOCKER ?= docker | |||
GIT ?= git | |||
# Build environment | |||
OS ?= $(shell $(GO) env GOHOSTOS) | |||
ARCH ?= $(shell $(GO) env GOHOSTARCH) | |||
OS_ARCH := $(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) | |||
# 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)) | |||
TMP_DIR := $(shell mktemp -d) | |||
# Test targets used for compatibility testing | |||
TARGET_TEST_COMPAT=boring picotls tstclnt | |||
# Some target-specific constants | |||
BORINGSSL_REVISION=1530ef3e | |||
BORINGSSL_REVISION=ff433815b51c34496bb6bea13e73e29e5c278238 | |||
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 | |||
@@ -48,16 +57,29 @@ $(BUILD_DIR)/$(OS_ARCH)/.ok_$(VER_OS_ARCH): clean | |||
# Apply additional patches | |||
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 | |||
GOARCH=$(ARCH) GOROOT="$(GOROOT_LOCAL)" $(GO) install -v std | |||
ifeq ($(INSTALL_RACE),1) | |||
GOARCH=$(ARCH) GOROOT="$(GOROOT_LOCAL)" $(GO) install -race -v std | |||
endif | |||
rm -rf $(TMP_DIR) | |||
@touch "$@" | |||
build-test-%: $(BUILD_DIR)/$(OS_ARCH)/.ok_$(VER_OS_ARCH) | |||
$(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-test-tris-client \ | |||
@@ -112,5 +134,7 @@ clean: | |||
clean-all: clean | |||
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 |
@@ -1,4 +1,4 @@ | |||
FROM golang:1.10-alpine | |||
FROM golang:1.11-alpine | |||
RUN apk add --update \ | |||
git \ | |||
@@ -10,8 +10,8 @@ RUN apk add --update \ | |||
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 | |||
#ARG REVISION=3f5e87d6a1931b6f6930e4eadb7b2d0b2aa7c588 | |||
@@ -20,10 +20,19 @@ RUN git clone https://github.com/FiloSottile/crypto-tls-bogo-shim \ | |||
#ARG REVISION=81cc32b846c9fe2ea32613287e57a6a0db7bbb9a | |||
# 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 | |||
WORKDIR /go/src/github.com/FiloSottile/crypto-tls-bogo-shim | |||
WORKDIR /go/src/github.com/henrydcase/crypto-tls-bogo-shim | |||
CMD ["make", "run"] |
@@ -3,6 +3,7 @@ FROM alpine | |||
RUN apk add --update \ | |||
git \ | |||
cmake \ | |||
patch \ | |||
perl \ | |||
python \ | |||
build-base \ | |||
@@ -34,18 +35,30 @@ RUN mkdir boringssl/build | |||
# ARG REVISION=89917a5 | |||
# Draft 18 | |||
#ARG REVISION=9b885c5 | |||
# ARG REVISION=9b885c5 | |||
# Draft 18, but with "bssl server -loop -www" support and build fix | |||
ARG REVISION=40b24c8154 | |||
# ARG REVISION=40b24c8154 | |||
# Draft 21 | |||
#ARG REVISION=cd8470f | |||
# ARG REVISION=cd8470f | |||
# 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 checkout $REVISION | |||
RUN cd boringssl && patch -p1 < /sidh_$REVISION.patch | |||
RUN cd boringssl/build && cmake -GNinja .. | |||
RUN cd boringssl && ninja -C build | |||
@@ -1,31 +1,34 @@ | |||
-----BEGIN CERTIFICATE----- | |||
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 | |||
-----END CERTIFICATE----- |
@@ -1,24 +1,31 @@ | |||
-----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----- |
@@ -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,7 +2,7 @@ | |||
set -e | |||
/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 \ | |||
-tls13-variant draft22 -session-in /session -connect "$@" < /httpreq.txt | |||
-session-in /session -connect "$@" < /httpreq.txt | |||
@@ -6,22 +6,27 @@ set -x | |||
bssl server \ | |||
-key rsa.pem \ | |||
-min-version tls1.2 -max-version tls1.3 \ | |||
-tls13-draft22-variant \ | |||
-accept 1443 -loop -www 2>&1 & | |||
# ECDSA | |||
bssl server \ | |||
-key ecdsa.pem \ | |||
-min-version tls1.2 -max-version tls1.3 \ | |||
-tls13-draft22-variant \ | |||
-accept 2443 -loop -www 2>&1 & | |||
# Require client authentication (with ECDSA) | |||
bssl server \ | |||
-key ecdsa.pem \ | |||
-min-version tls1.2 -max-version tls1.3 \ | |||
-tls13-draft22-variant \ | |||
-accept 6443 -loop -www \ | |||
-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 |
@@ -10,11 +10,17 @@ import time | |||
# Checks if TLS 1.3 was negotiated | |||
RE_PATTERN_HELLO_TLS_13_NORESUME = "^.*Hello TLS 1.3 \(draft .*\) _o/$|^.*Hello TLS 1.3 _o/$" | |||
# 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 | |||
RE_PATTERN_HELLO_0RTT = "^.*Hello TLS 1.3 .*\[resumed\] \[0-RTT\] _o/$" | |||
# Checks if 0-RTT was used and confirmed | |||
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): | |||
''' 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 ''' | |||
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): | |||
self.assertIsNotNone( | |||
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.server = self.d.run_server( | |||
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") | |||
@classmethod | |||
@@ -204,20 +209,37 @@ class InteropClient(object): | |||
# 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( | |||
InteropServer, | |||
@@ -226,13 +248,29 @@ class InteropServer_NSS( | |||
unittest.TestCase | |||
): 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( | |||
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") | |||
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__': | |||
unittest.main() |
@@ -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) | |||
} | |||
@@ -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 |
@@ -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) | |||
} |
@@ -8,6 +8,7 @@ EXPOSE 3443 | |||
EXPOSE 4443 | |||
EXPOSE 5443 | |||
EXPOSE 6443 | |||
EXPOSE 7443 | |||
ADD tris-localserver / | |||
ADD runner.sh / | |||
@@ -1,10 +1,11 @@ | |||
#!/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 |
@@ -2,15 +2,17 @@ package main | |||
import ( | |||
"crypto/tls" | |||
"encoding/hex" | |||
"crypto/x509" | |||
"errors" | |||
"flag" | |||
"fmt" | |||
"io" | |||
"io/ioutil" | |||
"log" | |||
"net/http" | |||
"os" | |||
"strings" | |||
"syscall" | |||
"time" | |||
"crypto/x509" | |||
) | |||
type ZeroRTT_t int | |||
@@ -18,149 +20,204 @@ type PubKeyAlgo_t int | |||
// Bitset | |||
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 { | |||
Address string | |||
ZeroRTT ZeroRTT_t | |||
PubKey PubKeyAlgo_t | |||
ClientAuthMethod tls.ClientAuthType | |||
Address string | |||
ZeroRTT ZeroRTT_t | |||
TLS tls.Config | |||
} | |||
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 { | |||
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 != "" { | |||
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 { | |||
log.Fatalf("Cannot open keylog file: %v", err) | |||
} | |||
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{ | |||
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("", "")) | |||
} | |||
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() | |||
@@ -212,50 +269,40 @@ ClMLEiNJQ0OMxAIaRtb2RehD4q3OWlpWf6joJ36PRBqL8T5+f2x6Tg3c64UR+QPX | |||
98UcCQHHdEhm7y2z5Z2Wt0B48tZ+UAxDEoEwMghNyw7wUD79IRlXGYypBnXaMuLX | |||
46aGxbsSQ7Rfg62Co3JG7vo+eJd0AoZHrtFUnfM8V70IFzMBZnSwRslHRJe56Q== | |||
-----END CERTIFICATE-----` | |||
rsaCa_client = `-----BEGIN CERTIFICATE----- | |||
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 | |||
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-----` | |||
ecdsaKey = `-----BEGIN EC PRIVATE KEY----- | |||
MHcCAQEEIFdhO7IW5UIwpB1e2Vunm9QyKvUHWcVwGfLjhpOajuR7oAoGCCqGSM49 | |||
AwEHoUQDQgAENM4HcjLONh8oKnYdbL4/dAfGF0FPgBQZiDBOJISPmmG6Y8hqEU1w | |||
5Jam03RTOVm8l00wyNWasVgr8WMXhpuQ0g== | |||
ecdsaCert = `` | |||
ecdsaKey = `` | |||
) |
@@ -3,6 +3,7 @@ package main | |||
import ( | |||
"crypto/tls" | |||
"crypto/x509" | |||
"errors" | |||
"flag" | |||
"fmt" | |||
"io" | |||
@@ -12,11 +13,10 @@ import ( | |||
) | |||
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{ | |||
@@ -27,28 +27,56 @@ var cipherSuiteIdToName = map[uint16]string{ | |||
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 { | |||
fmt.Printf("handshake failed: %v\n\n", err) | |||
c.failed++ | |||
failed++ | |||
return | |||
} | |||
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")) | |||
if err != nil { | |||
fmt.Printf("Write failed: %v\n\n", err) | |||
c.failed++ | |||
failed++ | |||
return | |||
} | |||
@@ -66,33 +94,45 @@ func (c *Client) run(addr string, version, cipherSuite uint16) { | |||
// is received right after reading data (observed with NSS selfserv). | |||
if !(n > 0 && err == io.EOF) && err != nil { | |||
fmt.Printf("Read failed: %v\n\n", err) | |||
c.failed++ | |||
failed++ | |||
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") | |||
} | |||
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() { | |||
var keylog_file string | |||
var keylog_file, tls_version, named_groups, named_ciphers string | |||
var enable_rsa, enable_ecdsa, client_auth bool | |||
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_ecdsa, "ecdsa", true, "Whether to enable ECDSA cipher suites") | |||
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() | |||
if flag.NArg() != 1 { | |||
flag.Usage() | |||
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 == "" { | |||
keylog_file = os.Getenv("SSLKEYLOGFILE") | |||
} | |||
@@ -101,129 +141,163 @@ func main() { | |||
if err != nil { | |||
log.Fatalf("Cannot open keylog file: %v", err) | |||
} | |||
client.KeyLogWriter = keylog_writer | |||
client.TLS.KeyLogWriter = keylog_writer | |||
log.Println("Enabled keylog") | |||
} | |||
if client_auth { | |||
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 { | |||
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") | |||
} | |||
} | |||
if enable_rsa { | |||
// 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 { | |||
// 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 limit supported groups? | |||
if client.failed > 0 { | |||
log.Fatalf("Failed handshakes: %d\n", client.failed) | |||
} else { | |||
fmt.Println("All handshakes passed") | |||
} | |||
result() | |||
} | |||
const ( | |||
client_ca = `-----BEGIN CERTIFICATE----- | |||
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 | |||
-----END 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-----` | |||
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-----` | |||
) | |||
@@ -21,7 +21,13 @@ ENV USE_64=1 NSS_ENABLE_TLS_1_3=1 | |||
# ARG REVISION=e61c0f657100 | |||
# 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 checkout -C $REVISION | |||
@@ -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 | |||
@@ -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 | |||
@@ -38,6 +38,7 @@ const ( | |||
alertInappropriateFallback alert = 86 | |||
alertUserCanceled alert = 90 | |||
alertNoRenegotiation alert = 100 | |||
alertUnsupportedExtension alert = 110 | |||
alertCertificateRequired alert = 116 | |||
alertNoApplicationProtocol alert = 120 | |||
alertSuccess alert = 255 // dummy value returned by unmarshal functions | |||
@@ -14,9 +14,11 @@ import ( | |||
) | |||
// 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. | |||
func pickSignatureAlgorithm(pubkey crypto.PublicKey, peerSigAlgs, ourSigAlgs []SignatureScheme, tlsVersion uint16) (SignatureScheme, uint8, crypto.Hash, error) { | |||
if tlsVersion < VersionTLS12 || len(peerSigAlgs) == 0 { | |||
@@ -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) | |||
} | |||
} | |||
} |
@@ -5,6 +5,7 @@ | |||
package tls | |||
import ( | |||
"crypto" | |||
"crypto/aes" | |||
"crypto/cipher" | |||
"crypto/des" | |||
@@ -12,7 +13,6 @@ import ( | |||
"crypto/rc4" | |||
"crypto/sha1" | |||
"crypto/sha256" | |||
"crypto/x509" | |||
"hash" | |||
"golang_org/x/crypto/chacha20poly1305" | |||
@@ -26,15 +26,15 @@ type keyAgreement interface { | |||
// In the case that the key agreement protocol doesn't use a | |||
// ServerKeyExchange message, generateServerKeyExchange can return 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. | |||
// This method may not be called if the server doesn't send a | |||
// 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 ( | |||
@@ -174,7 +174,10 @@ type fixedNonceAEAD struct { | |||
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) explicitNonceLen() int { return 8 } | |||
@@ -7,12 +7,12 @@ package tls | |||
import ( | |||
"container/list" | |||
"crypto" | |||
"crypto/internal/cipherhw" | |||
"crypto/rand" | |||
"crypto/sha512" | |||
"crypto/x509" | |||
"errors" | |||
"fmt" | |||
"internal/cpu" | |||
"io" | |||
"math/big" | |||
"net" | |||
@@ -22,14 +22,11 @@ import ( | |||
) | |||
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 ( | |||
@@ -40,7 +37,7 @@ const ( | |||
maxWarnAlertCount = 5 // maximum number of consecutive warning alerts | |||
minVersion = VersionTLS12 | |||
maxVersion = VersionTLS13Draft22 | |||
maxVersion = VersionTLS13 | |||
) | |||
// TLS record types. | |||
@@ -79,22 +76,25 @@ const ( | |||
// TLS extension numbers | |||
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 | |||
@@ -116,10 +116,15 @@ const ( | |||
type CurveID uint16 | |||
const ( | |||
// Exported IDs | |||
CurveP256 CurveID = 23 | |||
CurveP384 CurveID = 24 | |||
CurveP521 CurveID = 25 | |||
X25519 CurveID = 29 | |||
// Experimental KEX | |||
HybridSIDHp503Curve25519 CurveID = 0xFE30 | |||
HybridSIKEp503Curve25519 CurveID = 0xFE32 | |||
) | |||
// TLS 1.3 Key Share | |||
@@ -164,9 +169,10 @@ const ( | |||
// 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 ( | |||
signaturePKCS1v15 uint8 = iota + 1 | |||
signaturePKCS1v15 uint8 = iota + 16 | |||
signatureECDSA | |||
signatureRSAPSS | |||
) | |||
@@ -216,6 +222,7 @@ type ConnectionState struct { | |||
VerifiedChains [][]*x509.Certificate // verified chains built from PeerCertificates | |||
SignedCertificateTimestamps [][]byte // SCTs from the 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 | |||
// 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 | |||
serverCertificates []*x509.Certificate // Certificate chain presented by the server | |||
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 | |||
@@ -354,6 +362,10 @@ type ClientHelloInfo struct { | |||
// immediately available for Read. | |||
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. | |||
// 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. | |||
@@ -532,7 +544,8 @@ type Config struct { | |||
PreferServerCipherSuites bool | |||
// 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 | |||
// SessionTicketKey is used by TLS servers to provide session | |||
@@ -546,7 +559,7 @@ type Config struct { | |||
SessionTicketKey [32]byte | |||
// ClientSessionCache is a cache of ClientSessionState entries for TLS | |||
// session resumption. | |||
// session resumption. It is only used by clients. | |||
ClientSessionCache ClientSessionCache | |||
// MinVersion contains the minimum SSL/TLS version that is acceptable. | |||
@@ -607,6 +620,26 @@ type Config struct { | |||
// session tickets, instead of SessionTicketKey. | |||
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 | |||
// mutex protects sessionTicketKeys. | |||
@@ -616,6 +649,10 @@ type Config struct { | |||
// for new tickets and any subsequent keys can be used to decrypt old | |||
// tickets. | |||
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 | |||
@@ -683,7 +720,10 @@ func (c *Config) Clone() *Config { | |||
Accept0RTTData: c.Accept0RTTData, | |||
Max0RTTDataSize: c.Max0RTTDataSize, | |||
SessionTicketSealer: c.SessionTicketSealer, | |||
AcceptDelegatedCredential: c.AcceptDelegatedCredential, | |||
GetDelegatedCredential: c.GetDelegatedCredential, | |||
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 | |||
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 | |||
// current configuration. | |||
func (c *Config) getSupportedVersions() []uint16 { | |||
@@ -875,10 +909,6 @@ func (c *Config) getSupportedVersions() []uint16 { | |||
if maxVersion < minVersion { | |||
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] | |||
} | |||
@@ -1095,7 +1125,19 @@ func defaultTLS13CipherSuites() []uint16 { | |||
func initDefaultCipherSuites() { | |||
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 | |||
// cipher suites. | |||
topTLS13CipherSuites = []uint16{ | |||
@@ -1185,3 +1227,17 @@ func signatureFromSignatureScheme(signatureAlgorithm SignatureScheme) uint8 { | |||
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) | |||
} |
@@ -11,6 +11,7 @@ import ( | |||
"crypto/cipher" | |||
"crypto/subtle" | |||
"crypto/x509" | |||
"encoding/binary" | |||
"errors" | |||
"fmt" | |||
"io" | |||
@@ -51,17 +52,22 @@ type Conn struct { | |||
didResume bool // whether this connection was a session resumption | |||
cipherSuite uint16 | |||
ocspResponse []byte // stapled OCSP response | |||
scts [][]byte // signed certificate timestamps from server | |||
scts [][]byte // Signed certificate timestamps from server | |||
peerCertificates []*x509.Certificate | |||
// verifiedChains contains the certificate chains that we built, as | |||
// opposed to the ones presented by the server. | |||
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 string | |||
// secureRenegotiation is true if the server echoed the secure | |||
// renegotiation extension. (This is meaningless as a server because | |||
// renegotiation is not supported in that case.) | |||
secureRenegotiation bool | |||
// indicates wether extended MasterSecret extension is used (see RFC7627) | |||
useEMS bool | |||
// clientFinishedIsFirst is true if the client sent the first Finished | |||
// 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[12] = byte(n) | |||
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 | |||
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: | |||
c.XORKeyStream(payload, payload) | |||
case aead: | |||
// explicitIVLen is always 0 for TLS1.3 | |||
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] | |||
if len(nonce) == 0 { | |||
nonce = hc.seq[:] | |||
} | |||
payload = b.data[recordHeaderLen+explicitIVLen:] | |||
payload = payload[:payloadLen] | |||
var additionalData []byte | |||
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[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[:] | |||
} | |||
} 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] | |||
// opaque_type | |||
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) | |||
case cbcMode: | |||
blockSize := c.BlockSize() | |||
@@ -1663,6 +1684,9 @@ func (c *Conn) ConnectionState() ConnectionState { | |||
state.VerifiedChains = c.verifiedChains | |||
state.SignedCertificateTimestamps = c.scts | |||
state.OCSPResponse = c.ocspResponse | |||
if c.verifiedDc != nil { | |||
state.DelegatedCredential = c.verifiedDc.raw | |||
} | |||
state.HandshakeConfirmed = atomic.LoadInt32(&c.handshakeConfirmed) == 1 | |||
if !state.HandshakeConfirmed { | |||
state.Unique0RTTToken = c.binder | |||
@@ -11,6 +11,7 @@ import ( | |||
"net/http" | |||
"net/http/httptest" | |||
"os" | |||
"time" | |||
) | |||
// zeroSource is an io.Reader that returns an unlimited number of zero bytes. | |||
@@ -155,8 +156,80 @@ func ExampleConfig_keyLogWriter_TLS13() { | |||
// preferences. | |||
// 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("", "")) | |||
} |
@@ -146,16 +146,24 @@ func main() { | |||
if err != nil { | |||
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) | |||
if err != nil { | |||
log.Print("failed to open key.pem for writing:", err) | |||
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") | |||
} |
@@ -65,7 +65,9 @@ func makeClientHello(config *Config) (*clientHelloMsg, error) { | |||
supportedPoints: []uint8{pointFormatUncompressed}, | |||
nextProtoNeg: len(config.NextProtos) > 0, | |||
secureRenegotiationSupported: true, | |||
delegatedCredential: config.AcceptDelegatedCredential, | |||
alpnProtocols: config.NextProtos, | |||
extendedMSSupported: config.UseExtendedMasterSecret, | |||
} | |||
possibleCipherSuites := config.cipherSuites() | |||
hello.cipherSuites = make([]uint16, 0, len(possibleCipherSuites)) | |||
@@ -106,6 +108,7 @@ NextCipherSuite: | |||
hello.vers = VersionTLS12 | |||
hello.supportedVersions = config.getSupportedVersions() | |||
hello.supportedSignatureAlgorithms = supportedSignatureAlgorithms13 | |||
hello.supportedSignatureAlgorithmsCert = supportedSigAlgorithmsCert(supportedSignatureAlgorithms13) | |||
} | |||
return hello, nil | |||
@@ -191,7 +194,7 @@ func (c *Conn) clientHandshake() error { | |||
// Create one keyshare for the first default curve. If it is not | |||
// appropriate, the server should raise a HRR. | |||
defaultGroup := c.config.curvePreferences()[0] | |||
hs.privateKey, clientKS, err = c.config.generateKeyShare(defaultGroup) | |||
hs.privateKey, clientKS, err = c.generateKeyShare(defaultGroup) | |||
if err != nil { | |||
c.sendAlert(alertInternalError) | |||
return err | |||
@@ -411,6 +414,50 @@ func (hs *clientHandshakeState) processCertsFromServer(certificates [][]byte) er | |||
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 { | |||
c := hs.c | |||
@@ -476,10 +523,14 @@ func (hs *clientHandshakeState) doFullHandshake() error { | |||
keyAgreement := hs.suite.ka(c.vers) | |||
// Set the public key used to verify the handshake. | |||
pk := c.peerCertificates[0].PublicKey | |||
skx, ok := msg.(*serverKeyExchangeMsg) | |||
if ok { | |||
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 { | |||
c.sendAlert(alertUnexpectedMessage) | |||
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 { | |||
c.sendAlert(alertInternalError) | |||
return err | |||
@@ -539,6 +590,13 @@ func (hs *clientHandshakeState) doFullHandshake() error { | |||
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 { | |||
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() | |||
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 | |||
clientDidALPN := len(hs.hello.alpnProtocols) > 0 | |||
serverHasNPN := hs.serverHello.nextProtoNeg | |||
@@ -677,6 +739,10 @@ func (hs *clientHandshakeState) processServerHello() (bool, error) { | |||
return false, nil | |||
} | |||
if hs.session.useEMS != c.useEMS { | |||
return false, errors.New("differing EMS state") | |||
} | |||
if hs.session.vers != c.vers { | |||
c.sendAlert(alertHandshakeFailure) | |||
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, | |||
serverCertificates: c.peerCertificates, | |||
verifiedChains: c.verifiedChains, | |||
useEMS: c.useEMS, | |||
} | |||
return nil | |||
@@ -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") | |||
} | |||
} |
@@ -6,81 +6,100 @@ package tls | |||
import ( | |||
"bytes" | |||
"encoding/binary" | |||
"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:] | |||
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:] | |||
} | |||
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 | |||
func unmarshalExtensionSignatureAlgorithms(data []byte, length int) ([]SignatureScheme, alert) { | |||
if length < 2 || length&1 != 0 { | |||
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 | |||
} | |||
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 { | |||
@@ -104,12 +123,15 @@ func (m *clientHelloMsg) equal(i interface{}) bool { | |||
m.ticketSupported == m1.ticketSupported && | |||
bytes.Equal(m.sessionTicket, m1.sessionTicket) && | |||
eqSignatureAlgorithms(m.supportedSignatureAlgorithms, m1.supportedSignatureAlgorithms) && | |||
eqSignatureAlgorithms(m.supportedSignatureAlgorithmsCert, m1.supportedSignatureAlgorithmsCert) && | |||
m.secureRenegotiationSupported == m1.secureRenegotiationSupported && | |||
bytes.Equal(m.secureRenegotiation, m1.secureRenegotiation) && | |||
eqStrings(m.alpnProtocols, m1.alpnProtocols) && | |||
eqKeyShares(m.keyShares, m1.keyShares) && | |||
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 { | |||
@@ -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) | |||
numExtensions := 0 | |||
extensionsLength := 0 | |||
if m.nextProtoNeg { | |||
numExtensions++ | |||
} | |||
@@ -147,6 +170,10 @@ func (m *clientHelloMsg) marshal() []byte { | |||
extensionsLength += 2 + 2*len(m.supportedSignatureAlgorithms) | |||
numExtensions++ | |||
} | |||
if m.getSignatureAlgorithmsCert() != nil { | |||
extensionsLength += 2 + 2*len(m.getSignatureAlgorithmsCert()) | |||
numExtensions++ | |||
} | |||
if m.secureRenegotiationSupported { | |||
extensionsLength += 1 + len(m.secureRenegotiation) | |||
numExtensions++ | |||
@@ -179,6 +206,12 @@ func (m *clientHelloMsg) marshal() []byte { | |||
if m.earlyData { | |||
numExtensions++ | |||
} | |||
if m.delegatedCredential { | |||
numExtensions++ | |||
} | |||
if m.extendedMSSupported { | |||
numExtensions++ | |||
} | |||
if numExtensions > 0 { | |||
extensionsLength += 4 * numExtensions | |||
length += 2 + extensionsLength | |||
@@ -305,26 +338,15 @@ func (m *clientHelloMsg) marshal() []byte { | |||
copy(z, 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 { | |||
z[0] = byte(extensionRenegotiationInfo >> 8) | |||
z[1] = byte(extensionRenegotiationInfo & 0xff) | |||
@@ -407,6 +429,14 @@ func (m *clientHelloMsg) marshal() []byte { | |||
z[1] = byte(extensionEarlyData) | |||
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 | |||
@@ -471,6 +501,8 @@ func (m *clientHelloMsg) unmarshal(data []byte) alert { | |||
m.psks = nil | |||
m.pskKeyExchangeModes = nil | |||
m.earlyData = false | |||
m.delegatedCredential = false | |||
m.extendedMSSupported = false | |||
if len(data) == 0 { | |||
// ClientHello is optionally followed by extension data | |||
@@ -626,7 +658,7 @@ func (m *clientHelloMsg) unmarshal(data []byte) alert { | |||
return alertDecodeError | |||
} | |||
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 { | |||
return alertDecodeError | |||
} | |||
@@ -735,6 +767,15 @@ func (m *clientHelloMsg) unmarshal(data []byte) alert { | |||
case extensionEarlyData: | |||
// https://tools.ietf.org/html/draft-ietf-tls-tls13-18#section-4.2.8 | |||
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:] | |||
bindersOffset += length | |||
@@ -743,6 +784,10 @@ func (m *clientHelloMsg) unmarshal(data []byte) alert { | |||
return alertSuccess | |||
} | |||
func (m *clientHelloMsg) getSignatureAlgorithmsCert() []SignatureScheme { | |||
return signAlgosCertList(m.supportedSignatureAlgorithms, m.supportedSignatureAlgorithmsCert) | |||
} | |||
type serverHelloMsg struct { | |||
raw []byte | |||
vers uint16 | |||
@@ -763,6 +808,9 @@ type serverHelloMsg struct { | |||
keyShare keyShare | |||
psk bool | |||
pskIdentity uint16 | |||
// RFC7627 | |||
extendedMSSupported 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 && | |||
bytes.Equal(m.keyShare.data, m1.keyShare.data) && | |||
m.psk == m1.psk && | |||
m.pskIdentity == m1.pskIdentity | |||
m.pskIdentity == m1.pskIdentity && | |||
m.extendedMSSupported == m1.extendedMSSupported | |||
} | |||
func (m *serverHelloMsg) marshal() []byte { | |||
@@ -804,12 +853,7 @@ func (m *serverHelloMsg) marshal() []byte { | |||
return m.raw | |||
} | |||
oldTLS13Draft := m.vers >= VersionTLS13Draft18 && m.vers <= VersionTLS13Draft21 | |||
length := 38 + len(m.sessionId) | |||
if oldTLS13Draft { | |||
// no compression method, no session ID. | |||
length = 36 | |||
} | |||
numExtensions := 0 | |||
extensionsLength := 0 | |||
@@ -832,6 +876,9 @@ func (m *serverHelloMsg) marshal() []byte { | |||
extensionsLength += 1 + len(m.secureRenegotiation) | |||
numExtensions++ | |||
} | |||
if m.extendedMSSupported { | |||
numExtensions++ | |||
} | |||
if alpnLen := len(m.alpnProtocol); alpnLen > 0 { | |||
if alpnLen >= 256 { | |||
panic("invalid ALPN protocol") | |||
@@ -880,20 +927,13 @@ func (m *serverHelloMsg) marshal() []byte { | |||
} | |||
copy(x[6:38], m.random) | |||
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[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 { | |||
z[0] = byte(extensionsLength >> 8) | |||
@@ -976,7 +1016,6 @@ func (m *serverHelloMsg) marshal() []byte { | |||
z = z[len(sct)+2:] | |||
} | |||
} | |||
if m.keyShare.group != 0 { | |||
z[0] = uint8(extensionKeyShare >> 8) | |||
z[1] = uint8(extensionKeyShare) | |||
@@ -1000,6 +1039,10 @@ func (m *serverHelloMsg) marshal() []byte { | |||
z[5] = byte(m.pskIdentity) | |||
z = z[6:] | |||
} | |||
if m.extendedMSSupported { | |||
binary.BigEndian.PutUint16(z, extensionEMS) | |||
z = z[4:] | |||
} | |||
m.raw = x | |||
@@ -1012,29 +1055,19 @@ func (m *serverHelloMsg) unmarshal(data []byte) alert { | |||
} | |||
m.raw = data | |||
m.vers = uint16(data[4])<<8 | uint16(data[5]) | |||
oldTLS13Draft := m.vers >= VersionTLS13Draft18 && m.vers <= VersionTLS13Draft21 | |||
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 { | |||
return alertDecodeError | |||
} | |||
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.nextProtos = nil | |||
@@ -1046,6 +1079,7 @@ func (m *serverHelloMsg) unmarshal(data []byte) alert { | |||
m.keyShare.data = nil | |||
m.psk = false | |||
m.pskIdentity = 0 | |||
m.extendedMSSupported = false | |||
if len(data) == 0 { | |||
// ServerHello is optionally followed by extension data | |||
@@ -1069,7 +1103,11 @@ func (m *serverHelloMsg) unmarshal(data []byte) alert { | |||
if m.vers != VersionTLS12 { | |||
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 { | |||
@@ -1183,6 +1221,8 @@ func (m *serverHelloMsg) unmarshal(data []byte) alert { | |||
} | |||
m.psk = true | |||
m.pskIdentity = uint16(data[0])<<8 | uint16(data[1]) | |||
case extensionEMS: | |||
m.extendedMSSupported = true | |||
} | |||
data = data[length:] | |||
} | |||
@@ -1406,9 +1446,10 @@ func (m *certificateMsg) unmarshal(data []byte) alert { | |||
} | |||
type certificateEntry struct { | |||
data []byte | |||
ocspStaple []byte | |||
sctList [][]byte | |||
data []byte | |||
ocspStaple []byte | |||
sctList [][]byte | |||
delegatedCredential []byte | |||
} | |||
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 = ok && bytes.Equal(m.certificates[i].ocspStaple, m1.certificates[i].ocspStaple) | |||
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 { | |||
return false | |||
} | |||
@@ -1456,6 +1498,9 @@ func (m *certificateMsg13) marshal() (x []byte) { | |||
i += 2 + len(sct) | |||
} | |||
} | |||
if len(cert.delegatedCredential) != 0 { | |||
i += 4 + len(cert.delegatedCredential) | |||
} | |||
} | |||
length := 3 + 3*len(m.certificates) + i | |||
@@ -1530,6 +1575,15 @@ func (m *certificateMsg13) marshal() (x []byte) { | |||
sctLenPos[2] = uint8(sctLen >> 8) | |||
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[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]) | |||
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 { | |||
return alertDecodeError | |||
} | |||
return alertSuccess | |||
} | |||
type certificateRequestMsg13 struct { | |||
raw []byte | |||
requestContext []byte | |||
supportedSignatureAlgorithms []SignatureScheme | |||
certificateAuthorities [][]byte | |||
requestContext []byte | |||
supportedSignatureAlgorithms []SignatureScheme | |||
supportedSignatureAlgorithmsCert []SignatureScheme | |||
certificateAuthorities [][]byte | |||
} | |||
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.requestContext, m1.requestContext) && | |||
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) { | |||
@@ -2104,6 +2163,11 @@ func (m *certificateRequestMsg13) marshal() (x []byte) { | |||
numExtensions := 1 | |||
extensionsLength := 2 + 2*len(m.supportedSignatureAlgorithms) | |||
if m.getSignatureAlgorithmsCert() != nil { | |||
numExtensions += 1 | |||
extensionsLength += 2 + 2*len(m.getSignatureAlgorithmsCert()) | |||
} | |||
casLength := 0 | |||
if len(m.certificateAuthorities) > 0 { | |||
for _, ca := range m.certificateAuthorities { | |||
@@ -2131,7 +2195,11 @@ func (m *certificateRequestMsg13) marshal() (x []byte) { | |||
z = z[2:] | |||
// 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 | |||
if casLength > 0 { | |||
z[0] = byte(extensionCAs >> 8) | |||
@@ -2204,6 +2272,12 @@ func (m *certificateRequestMsg13) unmarshal(data []byte) alert { | |||
if err != alertSuccess { | |||
return err | |||
} | |||
case extensionSignatureAlgorithmsCert: | |||
var err alert | |||
m.supportedSignatureAlgorithmsCert, err = unmarshalExtensionSignatureAlgorithms(data, length) | |||
if err != alertSuccess { | |||
return err | |||
} | |||
case extensionCAs: | |||
// TODO DRY: share code with CH | |||
if length < 2 { | |||
@@ -2240,6 +2314,10 @@ func (m *certificateRequestMsg13) unmarshal(data []byte) alert { | |||
return alertSuccess | |||
} | |||
func (m *certificateRequestMsg13) getSignatureAlgorithmsCert() []SignatureScheme { | |||
return signAlgosCertList(m.supportedSignatureAlgorithms, m.supportedSignatureAlgorithmsCert) | |||
} | |||
type certificateVerifyMsg struct { | |||
raw []byte | |||
hasSignatureAndHash bool | |||
@@ -162,7 +162,7 @@ func (*clientHelloMsg) Generate(rand *rand.Rand, size int) reflect.Value { | |||
m.keyShares = make([]keyShare, rand.Intn(4)) | |||
for i := range m.keyShares { | |||
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)) | |||
for i := range m.supportedVersions { | |||
@@ -171,7 +171,12 @@ func (*clientHelloMsg) Generate(rand *rand.Rand, size int) reflect.Value { | |||
if rand.Intn(10) > 5 { | |||
m.earlyData = true | |||
} | |||
if rand.Intn(10) > 5 { | |||
m.delegatedCredential = true | |||
} | |||
if rand.Intn(10) > 5 { | |||
m.extendedMSSupported = true | |||
} | |||
return reflect.ValueOf(m) | |||
} | |||
@@ -199,19 +204,22 @@ func (*serverHelloMsg) Generate(rand *rand.Rand, size int) reflect.Value { | |||
if rand.Intn(10) > 5 { | |||
m.ticketSupported = true | |||
} | |||
if rand.Intn(10) > 5 { | |||
m.extendedMSSupported = true | |||
} | |||
m.alpnProtocol = randomString(rand.Intn(32)+1, rand) | |||
if rand.Intn(10) > 5 { | |||
numSCTs := rand.Intn(4) | |||
m.scts = make([][]byte, numSCTs) | |||
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 { | |||
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 { | |||
m.psk = true | |||
@@ -344,6 +352,9 @@ func (*sessionState) Generate(rand *rand.Rand, size int) reflect.Value { | |||
s.vers = uint16(rand.Intn(10000)) | |||
s.cipherSuite = uint16(rand.Intn(10000)) | |||
s.masterSecret = randomBytes(rand.Intn(100), rand) | |||
if rand.Intn(10) > 5 { | |||
s.usedEMS = true | |||
} | |||
numCerts := rand.Intn(20) | |||
s.certificates = make([][]byte, numCerts) | |||
for i := 0; i < numCerts; i++ { | |||
@@ -16,10 +16,6 @@ import ( | |||
"sync/atomic" | |||
) | |||
type Committer interface { | |||
Commit() error | |||
} | |||
// serverHandshakeState contains details of a server handshake in progress. | |||
// It's discarded once the handshake has completed. | |||
type serverHandshakeState struct { | |||
@@ -30,6 +26,11 @@ type serverHandshakeState struct { | |||
clientHello *clientHelloMsg | |||
hello *serverHelloMsg | |||
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 | |||
ellipticOk bool | |||
@@ -276,10 +277,10 @@ Curves: | |||
if len(hs.clientHello.alpnProtocols) > 0 { | |||
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 | |||
} else { | |||
hs.hello.alpnProtocol = selectedProto | |||
} | |||
c.clientProtocol = selectedProto | |||
} | |||
@@ -299,11 +300,36 @@ Curves: | |||
c.sendAlert(alertInternalError) | |||
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 | |||
} | |||
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) { | |||
case *ecdsa.PublicKey: | |||
hs.ecdsaOk = true | |||
@@ -314,7 +340,7 @@ Curves: | |||
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) { | |||
case *rsa.PublicKey: | |||
hs.rsaDecryptOk = true | |||
@@ -383,6 +409,11 @@ func (hs *serverHandshakeState) checkForResumption() bool { | |||
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 | |||
// Check that the client is still offering the ciphersuite in the session. | |||
for _, id := range hs.clientHello.cipherSuites { | |||
@@ -420,6 +451,7 @@ func (hs *serverHandshakeState) doResumeHandshake() error { | |||
// that we're doing a resumption. | |||
hs.hello.sessionId = hs.clientHello.sessionId | |||
hs.hello.ticketSupported = hs.sessionState.usedOldKey | |||
hs.hello.extendedMSSupported = hs.clientHello.extendedMSSupported && c.config.UseExtendedMasterSecret | |||
hs.finishedHash = newFinishedHash(c.vers, hs.suite) | |||
hs.finishedHash.discardHandshakeBuffer() | |||
hs.finishedHash.Write(hs.clientHello.marshal()) | |||
@@ -435,6 +467,7 @@ func (hs *serverHandshakeState) doResumeHandshake() error { | |||
} | |||
hs.masterSecret = hs.sessionState.masterSecret | |||
c.useEMS = hs.sessionState.usedEMS | |||
return nil | |||
} | |||
@@ -448,6 +481,7 @@ func (hs *serverHandshakeState) doFullHandshake() error { | |||
hs.hello.ticketSupported = hs.clientHello.ticketSupported && !c.config.SessionTicketsDisabled | |||
hs.hello.cipherSuite = hs.suite.id | |||
hs.hello.extendedMSSupported = hs.clientHello.extendedMSSupported && c.config.UseExtendedMasterSecret | |||
hs.finishedHash = newFinishedHash(hs.c.vers, hs.suite) | |||
if c.config.ClientAuth == NoClientCert { | |||
@@ -479,7 +513,7 @@ func (hs *serverHandshakeState) doFullHandshake() error { | |||
} | |||
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 { | |||
c.sendAlert(alertHandshakeFailure) | |||
return err | |||
@@ -544,15 +578,6 @@ func (hs *serverHandshakeState) doFullHandshake() error { | |||
} | |||
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) | |||
if err != nil { | |||
return err | |||
@@ -572,7 +597,7 @@ func (hs *serverHandshakeState) doFullHandshake() error { | |||
} | |||
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 == errClientKeyExchange { | |||
c.sendAlert(alertDecodeError) | |||
@@ -581,7 +606,8 @@ func (hs *serverHandshakeState) doFullHandshake() error { | |||
} | |||
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 { | |||
c.sendAlert(alertInternalError) | |||
return err | |||
@@ -711,6 +737,7 @@ func (hs *serverHandshakeState) sendSessionTicket() error { | |||
cipherSuite: hs.suite.id, | |||
masterSecret: hs.masterSecret, | |||
certificates: hs.certsFromClient, | |||
usedEMS: c.useEMS, | |||
} | |||
m.ticket, err = c.encryptTicket(state.marshal()) | |||
if err != nil { | |||
@@ -751,6 +778,15 @@ func (hs *serverHandshakeState) sendFinished(out []byte) error { | |||
func (hs *serverHandshakeState) processCertsFromClient(certificates [][]byte) (crypto.PublicKey, error) { | |||
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 | |||
certs := make([]*x509.Certificate, len(certificates)) | |||
var err error | |||
@@ -880,16 +916,17 @@ func (hs *serverHandshakeState) clientHelloInfo() *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 | |||
@@ -394,8 +394,6 @@ func TestSCTHandshake(t *testing.T) { | |||
PrivateKey: testRSAPrivateKey, | |||
SignedCertificateTimestamps: expected, | |||
}}, | |||
// See GH#76 | |||
MaxVersion: VersionTLS12, | |||
} | |||
clientConfig := &Config{ | |||
InsecureSkipVerify: true, | |||
@@ -10,7 +10,6 @@ import ( | |||
"crypto/md5" | |||
"crypto/rsa" | |||
"crypto/sha1" | |||
"crypto/x509" | |||
"errors" | |||
"io" | |||
"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. | |||
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 | |||
} | |||
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 { | |||
return nil, errClientKeyExchange | |||
} | |||
@@ -42,7 +41,7 @@ func (ka rsaKeyAgreement) processClientKeyExchange(config *Config, cert *Certifi | |||
} | |||
ciphertext = ckx.ciphertext[2:] | |||
} | |||
priv, ok := cert.PrivateKey.(crypto.Decrypter) | |||
priv, ok := sk.(crypto.Decrypter) | |||
if !ok { | |||
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 | |||
} | |||
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") | |||
} | |||
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[0] = byte(clientHello.vers >> 8) | |||
preMasterSecret[1] = byte(clientHello.vers) | |||
@@ -73,7 +72,7 @@ func (ka rsaKeyAgreement) generateClientKeyExchange(config *Config, clientHello | |||
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 { | |||
return nil, nil, err | |||
} | |||
@@ -156,7 +155,7 @@ type ecdheKeyAgreement struct { | |||
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() | |||
NextCandidate: | |||
@@ -207,7 +206,7 @@ NextCandidate: | |||
serverECDHParams[3] = byte(len(ecdhePublic)) | |||
copy(serverECDHParams[4:], ecdhePublic) | |||
priv, ok := cert.PrivateKey.(crypto.Signer) | |||
priv, ok := sk.(crypto.Signer) | |||
if !ok { | |||
return nil, errors.New("tls: certificate private key does not implement crypto.Signer") | |||
} | |||
@@ -255,7 +254,7 @@ NextCandidate: | |||
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 { | |||
return nil, errClientKeyExchange | |||
} | |||
@@ -291,7 +290,7 @@ func (ka *ecdheKeyAgreement) processClientKeyExchange(config *Config, cert *Cert | |||
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 { | |||
return errServerKeyExchange | |||
} | |||
@@ -337,7 +336,7 @@ func (ka *ecdheKeyAgreement) processServerKeyExchange(config *Config, clientHell | |||
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 { | |||
return err | |||
} | |||
@@ -355,10 +354,10 @@ func (ka *ecdheKeyAgreement) processServerKeyExchange(config *Config, clientHell | |||
if err != nil { | |||
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 { | |||
return nil, nil, errors.New("tls: missing ServerKeyExchange message") | |||
} | |||
@@ -117,6 +117,7 @@ var masterSecretLabel = []byte("master secret") | |||
var keyExpansionLabel = []byte("key expansion") | |||
var clientFinishedLabel = []byte("client 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) { | |||
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 | |||
// 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 | |||
@@ -49,8 +49,9 @@ func TestKeysFromPreMasterSecret(t *testing.T) { | |||
in, _ := hex.DecodeString(test.preMasterSecret) | |||
clientRandom, _ := hex.DecodeString(test.clientRandom) | |||
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 { | |||
t.Errorf("#%d: bad master secret %s, want %s", i, s, test.masterSecret) | |||
continue | |||
@@ -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) | |||
} |
@@ -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----- | |||
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 | |||
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) | |||
} | |||
} | |||
} |
@@ -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----- |
@@ -0,0 +1,5 @@ | |||
-----BEGIN EC PRIVATE KEY----- | |||
MHcCAQEEIIrYSSNQFaA2Hwf1duRSxKtLYX5CB04fSeQ6tF1aY/PuoAoGCCqGSM49 | |||
AwEHoUQDQgAEPR3tU2Fta9ktY+6P9G0cWO+0kETA6SFs38GecTyudlHz6xvCdz8q | |||
EKTcWGekdmdDPsHloRNtsiCa697B2O9IFA== | |||
-----END EC PRIVATE KEY----- |
@@ -37,6 +37,7 @@ type SessionTicketSealer interface { | |||
type sessionState struct { | |||
vers uint16 | |||
cipherSuite uint16 | |||
usedEMS bool | |||
masterSecret []byte | |||
certificates [][]byte | |||
// 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 || | |||
s.usedEMS != s1.usedEMS || | |||
s.cipherSuite != s1.cipherSuite || | |||
!bytes.Equal(s.masterSecret, s1.masterSecret) { | |||
return false | |||
@@ -77,7 +79,12 @@ func (s *sessionState) marshal() []byte { | |||
ret := make([]byte, length) | |||
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[2] = byte(s.cipherSuite >> 8) | |||
x[3] = byte(s.cipherSuite) | |||
@@ -108,8 +115,9 @@ func (s *sessionState) unmarshal(data []byte) alert { | |||
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.usedEMS = (data[0] & 0x80) == 0x80 | |||
masterSecretLen := int(data[4])<<8 | int(data[5]) | |||
data = data[6:] | |||
if len(data) < masterSecretLen { | |||
@@ -237,15 +237,14 @@ func X509KeyPair(certPEMBlock, keyPEMBlock []byte) (Certificate, error) { | |||
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 { | |||
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 { | |||
return fail(err) | |||
} | |||
@@ -674,7 +674,7 @@ func TestCloneNonFuncFields(t *testing.T) { | |||
switch fn := typ.Field(i).Name; fn { | |||
case "Rand": | |||
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 | |||
// function field to this list, you must also change | |||
// TestCloneFuncFields to ensure that the func field is | |||
@@ -713,6 +713,10 @@ func TestCloneNonFuncFields(t *testing.T) { | |||
f.Set(reflect.ValueOf(uint32(0))) | |||
case "SessionTicketSealer": | |||
// TODO | |||
case "AcceptDelegatedCredential": | |||
f.Set(reflect.ValueOf(false)) | |||
case "UseExtendedMasterSecret": | |||
f.Set(reflect.ValueOf(false)) | |||
default: | |||
t.Errorf("all fields must be accounted for, but saw unknown field %q", fn) | |||
} | |||