Автор | SHA1 | Съобщение | Дата |
---|---|---|---|
Henry Case |
a21fd9c1bc
|
refactors record encryption code (#122) | преди 6 години |
Henry D. Case | e81269b57e |
Revert "Small refactoring of record encryption code"
This reverts commit
|
преди 6 години |
Henry D. Case | 1782162852 | Small refactoring of record encryption code | преди 6 години |
Henry D. Case | ad86d61c42 | Let's use constant instead of hardcoding values (same is done in bssl) | преди 6 години |
Henry D. Case | 91a6fcebab | Cleanup | преди 6 години |
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 години |
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 години |
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 години |
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 години |
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 години |
Henry D. Case | 77d1fbf262 | Don't use VersionTLS13DraftXX anywhere | преди 6 години |
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 години |
Christopher Patton | 1ea9624098 |
Use static test data for testing delegated credentials
This removes dependency on NewDelegatedCredential from tris. |
преди 6 години |
Brendan Mc |
22d6deb0e7
|
Merge pull request #95 from cjpatton/subcerts
Delegated credentials for TLS |
преди 6 години |
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 години |
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 години |
Henry D. Case | 3ff71dcdc5 | tests: enable client authentication in bogo | преди 6 години |
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 години |
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 години |
Henry D. Case | 5bdf1af124 | TLS1.3 -draft23: Renumber key_share | преди 6 години |
Evan Klitzke | 67bc308e04 | Update client SCT list during TLS 1.3 handshake, fixes #76 | преди 6 години |
Henry D. Case | b1d6c0aeaa | Change function name verifyPeerCertificate->verifyPeerHandshakeSignature | преди 6 години |
Henry D. Case | 91d6db578b | CI: Fail build if code is wrongly formatted | преди 6 години |
Henry D. Case | b6af4dd343 | Code formatting for handshake_server_test.go | преди 6 години |
Henry D. Case | 3f720fc50c | Code formatting for TRIS test client and server | преди 6 години |
@@ -15,6 +15,9 @@ env: | |||
matrix: | |||
fast_finish: true | |||
before_install: | |||
- make -f _dev/Makefile fmtcheck | |||
install: | |||
- sudo pip install docker | |||
@@ -225,6 +225,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 | |||
@@ -328,7 +329,7 @@ func (hs *serverHandshakeState) readClientFinished13(hasConfirmLock bool) error | |||
return unexpectedMessageError(certVerify, msg) | |||
} | |||
err, alertCode := verifyPeerCertificate( | |||
err, alertCode := verifyPeerHandshakeSignature( | |||
certVerify, | |||
pubKey, | |||
supportedSignatureAlgorithms13, | |||
@@ -402,6 +403,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 +433,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 +478,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 { | |||
@@ -841,7 +852,7 @@ func (hs *clientHandshakeState) processEncryptedExtensions(ee *encryptedExtensio | |||
return nil | |||
} | |||
func verifyPeerCertificate( | |||
func verifyPeerHandshakeSignature( | |||
certVerify *certificateVerifyMsg, | |||
pubKey crypto.PublicKey, | |||
signAlgosKnown []SignatureScheme, | |||
@@ -953,6 +964,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 { | |||
@@ -1032,6 +1044,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 +1061,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 +1149,15 @@ 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 | |||
} |
@@ -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: | |||
``` | |||
@@ -23,7 +23,7 @@ INSTALL_RACE:= $(words $(filter $(ARCH)_$(shell go env CGO_ENABLED), amd64_1)) | |||
TARGET_TEST_COMPAT=boring picotls tstclnt | |||
# Some target-specific constants | |||
BORINGSSL_REVISION=1530ef3e | |||
BORINGSSL_REVISION=03de6813d8992a649092b4874ef0ebc022e2f58a | |||
BOGO_DOCKER_TRIS_LOCATION=/go/src/github.com/cloudflare/tls-tris | |||
############### | |||
@@ -112,5 +112,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 |
@@ -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"] |
@@ -34,15 +34,24 @@ 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 | |||
# Latest | |||
ARG REVISION=03de6813d8992a649092b4874ef0ebc022e2f58a | |||
RUN cd boringssl && git fetch | |||
RUN cd boringssl && git checkout $REVISION | |||
@@ -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 | |||
-tls13-variant draft28 -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 | |||
-tls13-variant draft28 -session-in /session -connect "$@" < /httpreq.txt | |||
@@ -6,21 +6,21 @@ set -x | |||
bssl server \ | |||
-key rsa.pem \ | |||
-min-version tls1.2 -max-version tls1.3 \ | |||
-tls13-draft22-variant \ | |||
-tls13-variant draft28 \ | |||
-accept 1443 -loop -www 2>&1 & | |||
# ECDSA | |||
bssl server \ | |||
-key ecdsa.pem \ | |||
-min-version tls1.2 -max-version tls1.3 \ | |||
-tls13-draft22-variant \ | |||
-tls13-variant draft28 \ | |||
-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 \ | |||
-tls13-variant draft28 \ | |||
-accept 6443 -loop -www \ | |||
-require-any-client-cert -debug 2>&1 & | |||
@@ -46,13 +46,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_DRAFT_TLS="\nsomestuff\nHello TLS 1.3 (draft 23) _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_RESUMED ="\nsomestuff\nHello TLS 1.3 (draft 23) [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( | |||
@@ -212,12 +212,14 @@ class InteropServer_BoringSSL( | |||
unittest.TestCase | |||
): CLIENT_NAME = "tls-tris:boring" | |||
class InteropServer_PicoTLS( | |||
InteropServer, | |||
ServerNominalMixin, | |||
ServerZeroRttMixin, | |||
unittest.TestCase | |||
): CLIENT_NAME = "tls-tris:picotls" | |||
# 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, | |||
@@ -2,6 +2,7 @@ package main | |||
import ( | |||
"crypto/tls" | |||
"crypto/x509" | |||
"encoding/hex" | |||
"flag" | |||
"fmt" | |||
@@ -10,7 +11,6 @@ import ( | |||
"net/http" | |||
"os" | |||
"time" | |||
"crypto/x509" | |||
) | |||
type ZeroRTT_t int | |||
@@ -18,21 +18,21 @@ type PubKeyAlgo_t int | |||
// Bitset | |||
const ( | |||
ZeroRTT_None ZeroRTT_t = 0 | |||
ZeroRTT_Offer = 1 << 0 | |||
ZeroRTT_Accept = 1 << 1 | |||
ZeroRTT_None ZeroRTT_t = 0 | |||
ZeroRTT_Offer = 1 << 0 | |||
ZeroRTT_Accept = 1 << 1 | |||
) | |||
const ( | |||
PubKeyRSA PubKeyAlgo_t = iota | |||
PubKeyRSA PubKeyAlgo_t = iota | |||
PubKeyECDSA | |||
) | |||
type server struct { | |||
Address string | |||
ZeroRTT ZeroRTT_t | |||
PubKey PubKeyAlgo_t | |||
ClientAuthMethod tls.ClientAuthType | |||
Address string | |||
ZeroRTT ZeroRTT_t | |||
PubKey PubKeyAlgo_t | |||
ClientAuthMethod tls.ClientAuthType | |||
} | |||
var tlsVersionToName = map[uint16]string{ | |||
@@ -43,14 +43,16 @@ var tlsVersionToName = map[uint16]string{ | |||
tls.VersionTLS13Draft18: "1.3 (draft 18)", | |||
tls.VersionTLS13Draft21: "1.3 (draft 21)", | |||
tls.VersionTLS13Draft22: "1.3 (draft 22)", | |||
tls.VersionTLS13Draft23: "1.3 (draft 23)", | |||
tls.VersionTLS13Draft28: "1.3 (draft 28)", | |||
} | |||
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.ClientAuthMethod = tls.NoClientCert | |||
s.ZeroRTT = ZeroRTT_None | |||
s.Address = "0.0.0.1:443" | |||
return s | |||
} | |||
func (s *server) start() { | |||
@@ -62,7 +64,7 @@ func (s *server) start() { | |||
log.Fatal(err) | |||
} | |||
var Max0RTTDataSize uint32 | |||
if ((s.ZeroRTT&ZeroRTT_Offer) == ZeroRTT_Offer) { | |||
if (s.ZeroRTT & ZeroRTT_Offer) == ZeroRTT_Offer { | |||
Max0RTTDataSize = 100 * 1024 | |||
} | |||
var keyLogWriter io.Writer | |||
@@ -82,7 +84,7 @@ func (s *server) start() { | |||
TLSConfig: &tls.Config{ | |||
Certificates: []tls.Certificate{cert}, | |||
Max0RTTDataSize: Max0RTTDataSize, | |||
Accept0RTTData: (s.ZeroRTT&ZeroRTT_Accept) == ZeroRTT_Accept, | |||
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. | |||
@@ -91,7 +93,7 @@ func (s *server) start() { | |||
}, | |||
MaxVersion: tls.VersionTLS13, | |||
ClientAuth: s.ClientAuthMethod, | |||
ClientCAs: clientCAs, | |||
ClientCAs: clientCAs, | |||
}, | |||
} | |||
log.Fatal(httpServer.ListenAndServeTLS("", "")) | |||
@@ -99,32 +101,32 @@ func (s *server) start() { | |||
func main() { | |||
s := NewServer() | |||
s := NewServer() | |||
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() | |||
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() | |||
s.Address=*arg_addr | |||
s.Address = *arg_addr | |||
if *arg_palg == "ecdsa" { | |||
s.PubKey = PubKeyECDSA | |||
} | |||
if *arg_palg == "ecdsa" { | |||
s.PubKey = PubKeyECDSA | |||
} | |||
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 | |||
} | |||
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 | |||
} | |||
if *arg_clientauth { | |||
s.ClientAuthMethod = tls.RequireAndVerifyClientCert | |||
} | |||
if *arg_clientauth { | |||
s.ClientAuthMethod = tls.RequireAndVerifyClientCert | |||
} | |||
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { | |||
tlsConn := r.Context().Value(http.TLSConnContextKey).(*tls.Conn) | |||
@@ -212,7 +214,7 @@ ClMLEiNJQ0OMxAIaRtb2RehD4q3OWlpWf6joJ36PRBqL8T5+f2x6Tg3c64UR+QPX | |||
98UcCQHHdEhm7y2z5Z2Wt0B48tZ+UAxDEoEwMghNyw7wUD79IRlXGYypBnXaMuLX | |||
46aGxbsSQ7Rfg62Co3JG7vo+eJd0AoZHrtFUnfM8V70IFzMBZnSwRslHRJe56Q== | |||
-----END CERTIFICATE-----` | |||
rsaCa_client = `-----BEGIN CERTIFICATE----- | |||
rsaCa_client = `-----BEGIN CERTIFICATE----- | |||
MIIFYDCCA0igAwIBAgIJAPpBgIvtQb1EMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV | |||
BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX | |||
aWRnaXRzIFB0eSBMdGQwHhcNMTgwMjEzMjAxNjA3WhcNMTkwMjEzMjAxNjA3WjBF | |||
@@ -17,6 +17,8 @@ var tlsVersionToName = map[uint16]string{ | |||
tls.VersionTLS12: "1.2", | |||
tls.VersionTLS13: "1.3", | |||
tls.VersionTLS13Draft18: "1.3 (draft 18)", | |||
tls.VersionTLS13Draft23: "1.3 (draft 23)", | |||
tls.VersionTLS13Draft28: "1.3 (draft 28)", | |||
} | |||
var cipherSuiteIdToName = map[uint16]string{ | |||
@@ -28,8 +30,8 @@ var cipherSuiteIdToName = map[uint16]string{ | |||
} | |||
type Client struct { | |||
KeyLogWriter io.Writer | |||
failed uint | |||
KeyLogWriter io.Writer | |||
failed uint | |||
client_cert tls.Certificate | |||
client_certpool *x509.CertPool | |||
} | |||
@@ -127,7 +129,6 @@ func main() { | |||
client.run(addr, tls.VersionTLS12, tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256) | |||
} | |||
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) | |||
@@ -226,4 +227,3 @@ LAoibwDU1NC8/3MfOBYMe6Qklu3kjexOJrfdo0Z7Khgd9F8A4tKwslUndSSlAfKF | |||
2rjfqabVMZMLZ2XEbA4W5JTfaZS4YYGcrjY7+i7OsnSxoYG2sb+xlQ== | |||
-----END RSA 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=09ab3310e710 | |||
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 | |||
@@ -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 } | |||
@@ -30,6 +30,8 @@ const ( | |||
VersionTLS13Draft18 = 0x7f00 | 18 | |||
VersionTLS13Draft21 = 0x7f00 | 21 | |||
VersionTLS13Draft22 = 0x7f00 | 22 | |||
VersionTLS13Draft23 = 0x7f00 | 23 | |||
VersionTLS13Draft28 = 0x7f00 | 28 | |||
) | |||
const ( | |||
@@ -40,7 +42,7 @@ const ( | |||
maxWarnAlertCount = 5 // maximum number of consecutive warning alerts | |||
minVersion = VersionTLS12 | |||
maxVersion = VersionTLS13Draft22 | |||
maxVersion = VersionTLS13Draft28 | |||
) | |||
// TLS record types. | |||
@@ -79,22 +81,24 @@ 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 | |||
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 | |||
@@ -216,6 +220,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 | |||
@@ -354,6 +359,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. | |||
@@ -607,6 +616,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. | |||
@@ -683,6 +712,8 @@ func (c *Config) Clone() *Config { | |||
Accept0RTTData: c.Accept0RTTData, | |||
Max0RTTDataSize: c.Max0RTTDataSize, | |||
SessionTicketSealer: c.SessionTicketSealer, | |||
AcceptDelegatedCredential: c.AcceptDelegatedCredential, | |||
GetDelegatedCredential: c.GetDelegatedCredential, | |||
sessionTicketKeys: sessionTicketKeys, | |||
} | |||
} | |||
@@ -858,7 +889,7 @@ var configSuppVersArray = [...]uint16{VersionTLS13, VersionTLS12, VersionTLS11, | |||
// with TLS 1.3 draft versions included. | |||
// | |||
// TODO: remove once TLS 1.3 is finalised. | |||
var tls13DraftSuppVersArray = [...]uint16{VersionTLS13Draft22, VersionTLS12, VersionTLS11, VersionTLS10, VersionSSL30} | |||
var tls13DraftSuppVersArray = [...]uint16{VersionTLS13Draft28, VersionTLS12, VersionTLS11, VersionTLS10, VersionSSL30} | |||
// getSupportedVersions returns the protocol versions that are supported by the | |||
// current configuration. | |||
@@ -1185,3 +1216,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,11 +52,14 @@ 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 | |||
@@ -355,6 +359,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,13 +470,8 @@ 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) | |||
nonce := b.data[recordHeaderLen : recordHeaderLen+explicitIVLen] | |||
if len(nonce) == 0 { | |||
nonce = hc.seq[:] | |||
@@ -475,18 +483,25 @@ func (hc *halfConn) encrypt(b *block, explicitIVLen int) (bool, alert) { | |||
if hc.version < VersionTLS13 { | |||
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[:] | |||
} | |||
b.resize(len(b.data) + c.Overhead()) | |||
} else { | |||
// 1 byte of content type is appended to payload and encrypted | |||
payload = append(payload, b.data[0]) | |||
if hc.version >= VersionTLS13 { | |||
// opaque type | |||
payload = payload[:len(payload)+1] | |||
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())) | |||
// make room for TLSCiphertext.encrypted_record | |||
b.resize(len(payload) + recordHeaderLen + c.Overhead()) | |||
} | |||
c.Seal(payload[:0], nonce, payload, additionalData) | |||
case cbcMode: | |||
blockSize := c.BlockSize() | |||
@@ -1663,6 +1678,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 | |||
@@ -155,8 +155,8 @@ 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 16ca97d21087a14d406b2601b4713dd82b156cc01d54665baaa4bdb62b72b9a4 | |||
// SERVER_HANDSHAKE_TRAFFIC_SECRET 0000000000000000000000000000000000000000000000000000000000000000 102c68d960da4f5e2b76a99636ac07bb5774e43b8ce8c14aa4dfd9bf54d11754 | |||
// SERVER_TRAFFIC_SECRET_0 0000000000000000000000000000000000000000000000000000000000000000 f3208d533bb885f32f52142acb484eed104739970c2f426e72a1ee31f6d28650 | |||
// CLIENT_TRAFFIC_SECRET_0 0000000000000000000000000000000000000000000000000000000000000000 70de6b1936df7db171c02f9cfdb04dfa9405a891c959beb15b86f26b2057ba23 | |||
} |
@@ -65,6 +65,7 @@ func makeClientHello(config *Config) (*clientHelloMsg, error) { | |||
supportedPoints: []uint8{pointFormatUncompressed}, | |||
nextProtoNeg: len(config.NextProtos) > 0, | |||
secureRenegotiationSupported: true, | |||
delegatedCredential: config.AcceptDelegatedCredential, | |||
alpnProtocols: config.NextProtos, | |||
} | |||
possibleCipherSuites := config.cipherSuites() | |||
@@ -106,6 +107,7 @@ NextCipherSuite: | |||
hello.vers = VersionTLS12 | |||
hello.supportedVersions = config.getSupportedVersions() | |||
hello.supportedSignatureAlgorithms = supportedSignatureAlgorithms13 | |||
hello.supportedSignatureAlgorithmsCert = supportedSigAlgorithmsCert(supportedSignatureAlgorithms13) | |||
} | |||
return hello, nil | |||
@@ -411,6 +413,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 +522,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 +578,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 | |||
@@ -6,81 +6,99 @@ 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 | |||
} | |||
// 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:] | |||
binary.BigEndian.PutUint16(data, (2*algNum)+2) // +1 for length | |||
data = data[2:] | |||
for _, sigAlgo := range sigSchemes { | |||
data[0] = byte(sigAlgo >> 8) | |||
data[1] = byte(sigAlgo) | |||
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 +122,14 @@ 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 | |||
} | |||
func (m *clientHelloMsg) marshal() []byte { | |||
@@ -120,6 +140,8 @@ 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 | |||
// Indicates wether to send signature_algorithms_cert extension | |||
if m.nextProtoNeg { | |||
numExtensions++ | |||
} | |||
@@ -147,6 +169,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 +205,9 @@ func (m *clientHelloMsg) marshal() []byte { | |||
if m.earlyData { | |||
numExtensions++ | |||
} | |||
if m.delegatedCredential { | |||
numExtensions++ | |||
} | |||
if numExtensions > 0 { | |||
extensionsLength += 4 * numExtensions | |||
length += 2 + extensionsLength | |||
@@ -305,26 +334,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 +425,10 @@ func (m *clientHelloMsg) marshal() []byte { | |||
z[1] = byte(extensionEarlyData) | |||
z = z[4:] | |||
} | |||
if m.delegatedCredential { | |||
binary.BigEndian.PutUint16(z, extensionDelegatedCredential) | |||
z = z[4:] | |||
} | |||
m.raw = x | |||
@@ -471,6 +493,7 @@ func (m *clientHelloMsg) unmarshal(data []byte) alert { | |||
m.psks = nil | |||
m.pskKeyExchangeModes = nil | |||
m.earlyData = false | |||
m.delegatedCredential = false | |||
if len(data) == 0 { | |||
// ClientHello is optionally followed by extension data | |||
@@ -735,6 +758,9 @@ 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 | |||
} | |||
data = data[length:] | |||
bindersOffset += length | |||
@@ -743,6 +769,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 | |||
@@ -976,7 +1006,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) | |||
@@ -1069,7 +1098,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 { | |||
@@ -1406,9 +1439,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 +1464,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 +1491,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 +1568,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 +1682,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 +2123,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 +2142,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 +2156,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 +2188,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 +2265,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 +2307,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 | |||
@@ -30,6 +30,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 | |||
@@ -299,11 +304,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 +344,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 | |||
@@ -479,7 +509,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 | |||
@@ -572,7 +602,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) | |||
@@ -880,16 +910,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 | |||
@@ -197,9 +197,9 @@ func TestDontSelectRSAWithECDSAKey(t *testing.T) { | |||
func TestRenegotiationExtension(t *testing.T) { | |||
clientHello := &clientHelloMsg{ | |||
vers: VersionTLS12, | |||
compressionMethods: []uint8{compressionNone}, | |||
random: make([]byte, 32), | |||
vers: VersionTLS12, | |||
compressionMethods: []uint8{compressionNone}, | |||
random: make([]byte, 32), | |||
secureRenegotiationSupported: true, | |||
cipherSuites: []uint16{TLS_RSA_WITH_RC4_128_SHA}, | |||
} | |||
@@ -394,8 +394,6 @@ func TestSCTHandshake(t *testing.T) { | |||
PrivateKey: testRSAPrivateKey, | |||
SignedCertificateTimestamps: expected, | |||
}}, | |||
// See GH#76 | |||
MaxVersion: VersionTLS12, | |||
} | |||
clientConfig := &Config{ | |||
InsecureSkipVerify: true, | |||
@@ -1004,7 +1002,7 @@ func TestFallbackSCSV(t *testing.T) { | |||
name: "FallbackSCSV", | |||
config: &serverConfig, | |||
// OpenSSL 1.0.1j is needed for the -fallback_scsv option. | |||
command: []string{"openssl", "s_client", "-fallback_scsv"}, | |||
command: []string{"openssl", "s_client", "-fallback_scsv"}, | |||
expectHandshakeErrorIncluding: "inappropriate protocol fallback", | |||
} | |||
runServerTestTLS11(t, test) | |||
@@ -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") | |||
} | |||
@@ -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,423 @@ | |||
// 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 | |||
} | |||
// Test data used for testing the TLS handshake with the delegated credential | |||
// extension. The PEM block encodes a DER encoded slice of dcTestDCs. | |||
// Use with maxVersion == VersionTLS13Draft28. | |||
// | |||
// TODO(henrydcase): Remove this when we drop support for draft28. | |||
const DcTestDataDraft28PEM = `-----BEGIN DC TEST DATA----- | |||
MIIIQjCCAUETCXRsczEzcDI1NgICfxwCAgQDBIGwAAk6gAQDfxwAAFswWTATBgcq | |||
hkjOPQIBBggqhkjOPQMBBwNCAASfXv9/jTDWOG9nwKmIN1GrFqF0p0frgMl6rxvy | |||
fu/58dkS0ZduzOUBG7qHsu+jHE8T29jH8SCH4Otl+3abna8IBAMARjBEAiAtDM7j | |||
w0bNce3QrVupL3wh5CUhIsTAwoYuWLls+1U8mwIgb/MHyZbcA7tALI0mNIJ1WRwy | |||
V7tByFYV21ataGTa+6UEeTB3AgEBBCDXxru/xm8LfdX+VVZBhBrb4kYrtVU28SNe | |||
q4TcMhvxUKAKBggqhkjOPQMBB6FEA0IABJ9e/3+NMNY4b2fAqYg3UasWoXSnR+uA | |||
yXqvG/J+7/nx2RLRl27M5QEbuoey76McTxPb2MfxIIfg62X7dpudrwgwggHsEwl0 | |||
bHMxM3A1MjECAn8cAgIGAwSB9AAJOoAGA38cAACeMIGbMBAGByqGSM49AgEGBSuB | |||
BAAjA4GGAAQBPRyZBgt3gNeSrgvhCGfzRJL7YH2nRdWZsi5ot+pDppu7GWwG2Bh7 | |||
Q8kurueZfyveEwQFnKOqUnqN/lXNxQuGAdcA3wg+Apb/ZjV+wQlaZjRFqCKWsp6A | |||
gFMPvab6nykiIrDxoJMtmk1+GW/YapaCwMiyBH6VRhqxQpEhR2ZXyXkqZ6EEAwBH | |||
MEUCIQDQgYRL6lqn+M/fTlPsXilqjwxF0x8TyDRYGd1tsg4wdAIgTvXu8lpzD2t4 | |||
vEqSKLRPA75HAU+ui1q4V8Hpudp7DkUEgd8wgdwCAQEEQgF3/A259KQTc+cw4ClJ | |||
pCnTXC9G2Fh5VULrAn3tFIpnzJ4VQun3UgkoPpeUSBdny9Kbd2DbfuFVd5YvNG2i | |||
HPxVBKAHBgUrgQQAI6GBiQOBhgAEAT0cmQYLd4DXkq4L4Qhn80SS+2B9p0XVmbIu | |||
aLfqQ6abuxlsBtgYe0PJLq7nmX8r3hMEBZyjqlJ6jf5VzcULhgHXAN8IPgKW/2Y1 | |||
fsEJWmY0RagilrKegIBTD72m+p8pIiKw8aCTLZpNfhlv2GqWgsDIsgR+lUYasUKR | |||
IUdmV8l5KmehMIIBQRMHYmFkdmVycwIDAP8AAgIEAwSBsQAJOoAEA/8AAABbMFkw | |||
EwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAESs4ZQnHHAPPHaA3uxyMAw91T4ajlJvL2 | |||
BAtP6XYpo9j+QWBtsFpwNRY85acAQJ9+7y1nbCHjn0UwB8Hi8P9pdQQDAEcwRQIg | |||
YJUpZPXZFbxyXDj/QYqvGlu4veHQJOaT0PL1rx6R/2gCIQC1qAAkNe5lz8W1M97t | |||
QXwxYRWgt8GLdBqp72EduVHtMgR5MHcCAQEEINU81qgDRzEPrx2YxJNBt7quCeA8 | |||
VZV9efsB7R7sxkwXoAoGCCqGSM49AwEHoUQDQgAESs4ZQnHHAPPHaA3uxyMAw91T | |||
4ajlJvL2BAtP6XYpo9j+QWBtsFpwNRY85acAQJ9+7y1nbCHjn0UwB8Hi8P9pdTCC | |||
AT8TBmJhZGtleQICfxwCAgQDBIGxAAk6gAQDfxwAAFswWTATBgcqhkjOPQIBBggq | |||
hkjOPQMBBwNCAAQnV8i/4ZrWoZG0nGDy6xsYzCV10FwaCbrvejTxcltSoCJ8HfPT | |||
u9FhOlHllmVyp/qCdB0ILsSlYDEFG9yzV/kGBAMARzBFAiBw3YabIamIHJAKmUcE | |||
+AZNsvBPuuYeKGCQ9N5n4/1hpwIhAJ07IU/p4+Nl24u4IneM9Fq5lL4YugiSAtDy | |||
/pWeCL0XBHkwdwIBAQQgOR6w5qkUyavY92PuOBXslfxJgfS8RUaAImqAlWhniKug | |||
CgYIKoZIzj0DAQehRANCAARH0kbf92XgJ5Mop4Spbpp3bjwzQw7Pg6T9vQH0q8Hy | |||
CTG65vcmu2whOu+0nR3eJg7rt9BhcHredcOoUhGbgqbRMIIBPhMGYmFkc2lnAgJ/ | |||
HAICBAMEgbAACTqABAN/HAAAWzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABBlb | |||
oANTnMd8jcnuzyCv+I+l51tqVog0wagYMo6L7A2RlTqgTYaz0p7mH3wsHfsv/Py8 | |||
Scv5o7vp/MIQjEbeg8wEAwBGMEQCIDozxK17n3gytnV9h6X9BKz5GsxBgr9+Ympe | |||
9XXppP57AiAPks17U0EhoIhSk6dhmVpgjkoHt9jxn1xYIwJxceGWywR5MHcCAQEE | |||
IH7GjuBRPz5WvrYrmD6dlCHX5Fda2C7faa+f0mmjkOfvoAoGCCqGSM49AwEHoUQD | |||
QgAEGVugA1Ocx3yNye7PIK/4j6XnW2pWiDTBqBgyjovsDZGVOqBNhrPSnuYffCwd | |||
+y/8/LxJy/mju+n8whCMRt6DzDCCAT8TBXRsczEyAgIDAwICBAMEgbIACTqABAMD | |||
AwAAWzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABFbRSfoqtGJdMb7NP3hENn6A | |||
b8tzLgr8Cj77JSoSVloy/+XOa+wz1OhEzA2b54WkEhVQor+RAT688z7UwEXFwWsE | |||
AwBIMEYCIQCdahwKMP01K5rvn3IU7JQElg1TjnGw1vZk7zsjg1B0gQIhAMLlhfUA | |||
Zd/eyMHutw9HfBOWX7rlcKN12RwtGuNXvZ1BBHkwdwIBAQQgSSNaIBwdPWauUSKg | |||
LN73E41eUQrWung1lwgTQWV1AhqgCgYIKoZIzj0DAQehRANCAARW0Un6KrRiXTG+ | |||
zT94RDZ+gG/Lcy4K/Ao++yUqElZaMv/lzmvsM9ToRMwNm+eFpBIVUKK/kQE+vPM+ | |||
1MBFxcFr | |||
-----END DC TEST DATA----- | |||
` | |||
// Use with maxVersion == VersionTLS13. | |||
const DcTestDataTLS13PEM = `-----BEGIN DC TEST DATA----- | |||
MIIIQzCCAUMTCXRsczEzcDI1NgICAwQCAgQDBIGyAAk6gAQDAwQAAFswWTATBgcq | |||
hkjOPQIBBggqhkjOPQMBBwNCAAQpQtUm8AWOzCN+aGUVsoKH9lZWNqkQCBGhpVtT | |||
u3ye6ACcwgNf81AYQ1ROb3EbWrnbvq9ap4a5QJ8AcrhZ9u0dBAMASDBGAiEA7LHb | |||
Fh+RDi9RTRjlP0+b2eP+4CDtuK0qKSjf4kFbJ9ICIQDB/XIXkLV6qLW70MhFWCUi | |||
2eqyhwtvTuMyATEJnyHKvwR5MHcCAQEEILHC94EWZnuVJqrbq3U+BnEU8BQPGfk6 | |||
pkB7mD8wqhl/oAoGCCqGSM49AwEHoUQDQgAEKULVJvAFjswjfmhlFbKCh/ZWVjap | |||
EAgRoaVbU7t8nugAnMIDX/NQGENUTm9xG1q5276vWqeGuUCfAHK4WfbtHTCCAesT | |||
CXRsczEzcDUyMQICAwQCAgYDBIHzAAk6gAYDAwQAAJ4wgZswEAYHKoZIzj0CAQYF | |||
K4EEACMDgYYABAHgWg5NSn/t/BBxU9uWVBwIz3NWfq2xo1eQMsJY1ui9ILtmFsLn | |||
QF1jbGrjlBZoh2sbHPFPl7yMOSYyVBFryhTaiQG7x11/Xs9fNC6AUm/6wROLMHTr | |||
qCkiqCjIKVtBaM8FCAfPLoJHzPUu/h79Q0IdBlVhl4nEa4cWVW34cECfT+YdjgQD | |||
AEYwRAIge+tF+cai/jfZtzUaVTcVuZfdIcGpRy4CfI2tKLipDCQCIAVigOh2jOFh | |||
QWbX4h4Vz3ULoIuM+3wsFad0S0oH1v9HBIHfMIHcAgEBBEIAzNpPpiTsrv+0a3oA | |||
CaGGr83/2Z632tygYjEOs919YrLR1Xe83hf5AvJLUz6u3RRlQdqwyPGQ1wm8baQ6 | |||
E0Pf6j+gBwYFK4EEACOhgYkDgYYABAHgWg5NSn/t/BBxU9uWVBwIz3NWfq2xo1eQ | |||
MsJY1ui9ILtmFsLnQF1jbGrjlBZoh2sbHPFPl7yMOSYyVBFryhTaiQG7x11/Xs9f | |||
NC6AUm/6wROLMHTrqCkiqCjIKVtBaM8FCAfPLoJHzPUu/h79Q0IdBlVhl4nEa4cW | |||
VW34cECfT+YdjjCCAUITB2JhZHZlcnMCAwD/AAICBAMEgbIACTqABAP/AAAAWzBZ | |||
MBMGByqGSM49AgEGCCqGSM49AwEHA0IABCPo5FSmarRgC/15bymE+3s4TXyQH9Oh | |||
nlcKbAR70jqWLr9jbyjT7dy09sr5B6cVlw8AU2TeojdRUNG7y4nKnLsEAwBIMEYC | |||
IQDZiMm7SoNMMvvrlxOF0OMSt1/hMOras702RDI2wvT92gIhAKgCmYucgBUIqMJd | |||
d6g2FcY9UZnPzvnSuX9uBm38RMLMBHkwdwIBAQQgnx2Os1Z5kbZo61ItkpwJ0khL | |||
7zgzLcc1X4unR3R56q+gCgYIKoZIzj0DAQehRANCAAQj6ORUpmq0YAv9eW8phPt7 | |||
OE18kB/ToZ5XCmwEe9I6li6/Y28o0+3ctPbK+QenFZcPAFNk3qI3UVDRu8uJypy7 | |||
MIIBPxMGYmFka2V5AgIDBAICBAMEgbEACTqABAMDBAAAWzBZMBMGByqGSM49AgEG | |||
CCqGSM49AwEHA0IABGGXD4Td3D7im9y0S1wGoFgL4afAiklkSlQcNus2XfGUJS4c | |||
io+gm4NBMcXby6LpN4lg5/0+K0i448WrIdd2eBYEAwBHMEUCIBMirxmjL9Yeigpl | |||
aeqHncrT4V2u+sYBqa+dUUCXDTaqAiEAuR2geInXmNRtGWVltZh1pnohvwloPVvu | |||
XK5qUb9g6/gEeTB3AgEBBCDk7f6Fto9m6vEDYiZapi2Hm8ranfS0AOgfnDfsRQa5 | |||
PKAKBggqhkjOPQMBB6FEA0IABFmA7YsXewnCF0R5eHLBwn4RsF1F5IwB8ZLpL2v4 | |||
GBD6YHmZDPBZ2/SZ3LxLGgT5yiO1/5y2ujDXsQ9X78ucHn8wggE+EwZiYWRzaWcC | |||
AgMEAgIEAwSBsAAJOoAEAwMEAABbMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE | |||
W2eqjqibupKlU/BwVWwfNE1qUdxqhF3cen0aKl8in24PcEi3AH1Y/zubsjoKah/q | |||
YUfcmgAvhvsSFqohWzMa5gQDAEYwRAIgT4Tm7648J1OuTrn+HAJXVfzoXbcL/QUx | |||
YxVDcpxytkoCIDulABj6w3EoQLoq8b1V781oPHKkUR7+L/SUPj/DxKQ2BHkwdwIB | |||
AQQgIAwscB81XCsAujU+tr75y7yMFfSLtFkPAzn3/GiXpoWgCgYIKoZIzj0DAQeh | |||
RANCAARbZ6qOqJu6kqVT8HBVbB80TWpR3GqEXdx6fRoqXyKfbg9wSLcAfVj/O5uy | |||
OgpqH+phR9yaAC+G+xIWqiFbMxrmMIIBPhMFdGxzMTICAgMDAgIEAwSBsQAJOoAE | |||
AwMDAABbMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEnsChIIFXdvdOTFnf3cyv | |||
MsHKpSy68X+SbepvhPg+MBrn+ly9mb+hWPp2j0UJKiXwQmMf4vicNOYyjreml8Hf | |||
VQQDAEcwRQIhANfDJ57MDLZqtye+uolguWx39vhfkvB9svEjYZwWTcoKAiALBgkH | |||
OoRxcalH9qbE2p6LHLszqYyYW312aTHHYF0/6QR5MHcCAQEEILFX1gHwKwJwAQI+ | |||
GNisTdlwN0clslAccLogW0ON0gAZoAoGCCqGSM49AwEHoUQDQgAEnsChIIFXdvdO | |||
TFnf3cyvMsHKpSy68X+SbepvhPg+MBrn+ly9mb+hWPp2j0UJKiXwQmMf4vicNOYy | |||
jreml8HfVQ== | |||
-----END DC TEST DATA-----` | |||
// Parses the input PEM block containing the test DCs. | |||
func dcLoadTestData(in []byte, out *[]dcTestDC) error { | |||
block, _ := pem.Decode(in) | |||
if block == nil { | |||
return errors.New("failed to decode DC tests PEM block") | |||
} | |||
// Parse the DER-encoded test DCs. | |||
_, err := asn1.Unmarshal(block.Bytes, out) | |||
if err != nil { | |||
return errors.New("failed to unmarshal DC test ASN.1 data") | |||
} | |||
// Check that the test data is for the right version. This should be | |||
// maxVersion, defined in common.go. | |||
for _, test := range *out { | |||
dc, err := unmarshalDelegatedCredential(test.DC) | |||
if err != nil { | |||
return err | |||
} | |||
// Sanity check that test version matches the version encoded by the DC. | |||
testVersion := uint16(test.Version) | |||
if dc.cred.expectedVersion != testVersion { | |||
return fmt.Errorf( | |||
"test version doesn't match credential version: got: 0x0%04x; want: 0x%04x", | |||
testVersion, dc.cred.expectedVersion) | |||
} | |||
// With the exception of "badvers" and "tsl12", all test DCs should have | |||
// the expected verison. | |||
if test.Name != "badvers" && test.Name != "tls12" && testVersion != maxVersion { | |||
return fmt.Errorf( | |||
"encountered test with wrong version: got: 0x0%04x; want: 0x%04x", | |||
test.Version, maxVersion) | |||
} | |||
} | |||
return nil | |||
} | |||
var dcTestDCs []dcTestDC | |||
var dcTestConfig *Config | |||
var dcTestDelegationCert Certificate | |||
var dcTestCert Certificate | |||
var dcTestNow time.Time | |||
func init() { | |||
// Load the DC test data. | |||
var testData []byte | |||
switch maxVersion { | |||
case VersionTLS13Draft28: | |||
testData = []byte(DcTestDataDraft28PEM) | |||
case 0x0304: // TODO(henrydcase): Fix once the final version is implemented | |||
testData = []byte(DcTestDataTLS13PEM) | |||
default: | |||
panic(fmt.Errorf("no test data for version %04x", maxVersion)) | |||
} | |||
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) | |||
} | |||
} | |||
} |
@@ -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,8 @@ func TestCloneNonFuncFields(t *testing.T) { | |||
f.Set(reflect.ValueOf(uint32(0))) | |||
case "SessionTicketSealer": | |||
// TODO | |||
case "AcceptDelegatedCredential": | |||
f.Set(reflect.ValueOf(false)) | |||
default: | |||
t.Errorf("all fields must be accounted for, but saw unknown field %q", fn) | |||
} | |||