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>
This commit is contained in:
David Benjamin 2016-07-09 14:26:01 -07:00
parent 615119a9e9
commit 8d343b44bb
3 changed files with 121 additions and 12 deletions

View File

@ -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.

View File

@ -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
}

View File

@ -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.