diff options
author | David Benjamin <davidben@google.com> | 2016-07-10 00:26:01 +0300 |
---|---|---|
committer | David Benjamin <davidben@google.com> | 2016-07-12 22:23:28 +0300 |
commit | 8d343b44bbab829d1a28fdef650ca95f7db4412e (patch) | |
tree | 40d6c190ba52a509d1093d434bf9b57940404dcf | |
parent | 615119a9e94093062e564d8a7ccc7013fc292048 (diff) |
Implement client certificates for TLS 1.3 in Go.
Tested by having client and server talk to each other. This adds the
certificate_extensions field to CertificateRequest which I'd previously
missed. (We completely ignore the field, with the expectation that the C
code won't have anything useful to do with it either.)
Change-Id: I74f96acd36747d4b6a6f533535e36ea8e94d2be8
Reviewed-on: https://boringssl-review.googlesource.com/8710
Reviewed-by: David Benjamin <davidben@google.com>
-rw-r--r-- | ssl/test/runner/handshake_client.go | 48 | ||||
-rw-r--r-- | ssl/test/runner/handshake_messages.go | 18 | ||||
-rw-r--r-- | ssl/test/runner/handshake_server.go | 67 |
3 files changed, 121 insertions, 12 deletions
diff --git a/ssl/test/runner/handshake_client.go b/ssl/test/runner/handshake_client.go index a67b2427..4ff70126 100644 --- a/ssl/test/runner/handshake_client.go +++ b/ssl/test/runner/handshake_client.go @@ -510,8 +510,7 @@ func (hs *clientHandshakeState) doTLS13Handshake() error { } var chainToSend *Certificate - var certRequested bool - var certRequestContext []byte + var certReq *certificateRequestMsg if hs.suite.flags&suitePSK != 0 { if encryptedExtensions.extensions.ocspResponse != nil { c.sendAlert(alertUnsupportedExtension) @@ -530,11 +529,10 @@ func (hs *clientHandshakeState) doTLS13Handshake() error { return err } - certReq, ok := msg.(*certificateRequestMsg) + var ok bool + certReq, ok = msg.(*certificateRequestMsg) if ok { hs.writeServerHash(certReq.marshal()) - certRequested = true - certRequestContext = certReq.requestContext chainToSend, err = selectClientCertificate(c, certReq) if err != nil { @@ -602,10 +600,42 @@ func (hs *clientHandshakeState) doTLS13Handshake() error { masterSecret := hs.finishedHash.extractKey(handshakeSecret, zeroSecret) trafficSecret := hs.finishedHash.deriveSecret(masterSecret, applicationTrafficLabel) - if certRequested { - _ = chainToSend - _ = certRequestContext - return errors.New("tls: client auth not implemented.") + if certReq != nil { + certMsg := &certificateMsg{ + hasRequestContext: true, + requestContext: certReq.requestContext, + } + if chainToSend != nil { + certMsg.certificates = chainToSend.Certificate + } + hs.writeClientHash(certMsg.marshal()) + c.writeRecord(recordTypeHandshake, certMsg.marshal()) + + if chainToSend != nil { + certVerify := &certificateVerifyMsg{ + hasSignatureAlgorithm: true, + } + + // Determine the hash to sign. + privKey := chainToSend.PrivateKey + + var err error + certVerify.signatureAlgorithm, err = selectSignatureAlgorithm(c.vers, privKey, c.config, certReq.signatureAlgorithms) + if err != nil { + c.sendAlert(alertInternalError) + return err + } + + input := hs.finishedHash.certificateVerifyInput(clientCertificateVerifyContextTLS13) + certVerify.signature, err = signMessage(c.vers, privKey, c.config, certVerify.signatureAlgorithm, input) + if err != nil { + c.sendAlert(alertInternalError) + return err + } + + hs.writeClientHash(certVerify.marshal()) + c.writeRecord(recordTypeHandshake, certVerify.marshal()) + } } // Send a client Finished message. diff --git a/ssl/test/runner/handshake_messages.go b/ssl/test/runner/handshake_messages.go index baa07bd5..0df3be32 100644 --- a/ssl/test/runner/handshake_messages.go +++ b/ssl/test/runner/handshake_messages.go @@ -1461,6 +1461,11 @@ func (m *certificateRequestMsg) marshal() []byte { caEntry.addBytes(ca) } + if m.hasRequestContext { + // Emit no certificate extensions. + body.addU16(0) + } + m.raw = builder.finish() return m.raw } @@ -1538,6 +1543,19 @@ func (m *certificateRequestMsg) unmarshal(data []byte) bool { m.certificateAuthorities = append(m.certificateAuthorities, cas[:caLen]) cas = cas[caLen:] } + + if m.hasRequestContext { + // Ignore certificate extensions. + if len(data) < 2 { + return false + } + extsLength := int(data[0])<<8 | int(data[1]) + if len(data) < 2+extsLength { + return false + } + data = data[2+extsLength:] + } + if len(data) > 0 { return false } diff --git a/ssl/test/runner/handshake_server.go b/ssl/test/runner/handshake_server.go index db0b3581..06823edf 100644 --- a/ssl/test/runner/handshake_server.go +++ b/ssl/test/runner/handshake_server.go @@ -406,8 +406,25 @@ Curves: if hs.suite.flags&suitePSK == 0 { if config.ClientAuth >= RequestClientCert { - // TODO(davidben): Implement client auth. - return errors.New("tls: client auth not implemented") + // Request a client certificate + certReq := &certificateRequestMsg{ + hasSignatureAlgorithm: true, + hasRequestContext: true, + } + if !config.Bugs.NoSignatureAlgorithms { + certReq.signatureAlgorithms = config.signSignatureAlgorithms() + } + + // An empty list of certificateAuthorities signals to + // the client that it may send any certificate in response + // to our request. When we know the CAs we trust, then + // we can send them down, so that the client can choose + // an appropriate certificate to give to us. + if config.ClientCAs != nil { + certReq.certificateAuthorities = config.ClientCAs.Subjects() + } + hs.writeServerHash(certReq.marshal()) + c.writeRecord(recordTypeHandshake, certReq.marshal()) } certMsg := &certificateMsg{ @@ -461,7 +478,51 @@ Curves: // If we requested a client certificate, then the client must send a // certificate message, even if it's empty. if config.ClientAuth >= RequestClientCert { - return errors.New("tls: client certificates not implemented") + msg, err := c.readHandshake() + if err != nil { + return err + } + + certMsg, ok := msg.(*certificateMsg) + if !ok { + c.sendAlert(alertUnexpectedMessage) + return unexpectedMessageError(certMsg, msg) + } + hs.writeClientHash(certMsg.marshal()) + + if len(certMsg.certificates) == 0 { + // The client didn't actually send a certificate + switch config.ClientAuth { + case RequireAnyClientCert, RequireAndVerifyClientCert: + c.sendAlert(alertBadCertificate) + return errors.New("tls: client didn't provide a certificate") + } + } + + pub, err := hs.processCertsFromClient(certMsg.certificates) + if err != nil { + return err + } + + if len(c.peerCertificates) > 0 { + msg, err = c.readHandshake() + if err != nil { + return err + } + + certVerify, ok := msg.(*certificateVerifyMsg) + if !ok { + c.sendAlert(alertUnexpectedMessage) + return unexpectedMessageError(certVerify, msg) + } + + input := hs.finishedHash.certificateVerifyInput(clientCertificateVerifyContextTLS13) + if err := verifyMessage(c.vers, pub, config, certVerify.signatureAlgorithm, input, certVerify.signature); err != nil { + c.sendAlert(alertBadCertificate) + return err + } + hs.writeClientHash(certVerify.marshal()) + } } // Read the client Finished message. |