Commit 8d343b44 authored by David Benjamin's avatar David Benjamin
Browse files

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: default avatarDavid Benjamin <[email protected]>
parent 615119a9
......@@ -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.
......
......@@ -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
}
......
......@@ -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.
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment