Implement OCSP stapling and SCT in Go TLS 1.3.
While the random connection property extensions like ALPN and SRTP
remain largely unchanged in TLS 1.3 (but for interaction with 0-RTT),
authentication-related extensions change significantly and need
dedicated logic.
Change-Id: I2588935c2563a22e9879fb81478b8df5168b43de
Reviewed-on: https://boringssl-review.googlesource.com/8602
Reviewed-by: David Benjamin <davidben@google.com>
diff --git a/ssl/test/runner/handshake_client.go b/ssl/test/runner/handshake_client.go
index 1ec5aab..5496fa2 100644
--- a/ssl/test/runner/handshake_client.go
+++ b/ssl/test/runner/handshake_client.go
@@ -497,9 +497,18 @@
var chainToSend *Certificate
var certRequested bool
var certRequestContext []byte
- if hs.suite.flags&suitePSK == 0 {
- // TODO(davidben): Save OCSP response and SCT list. Forbid them
- // if not negotiating a certificate-based extension.
+ if hs.suite.flags&suitePSK != 0 {
+ if encryptedExtensions.extensions.ocspResponse != nil {
+ c.sendAlert(alertUnsupportedExtension)
+ return errors.New("tls: server sent OCSP response without a certificate")
+ }
+ if encryptedExtensions.extensions.sctList != nil {
+ c.sendAlert(alertUnsupportedExtension)
+ return errors.New("tls: server sent SCT list without a certificate")
+ }
+ } else {
+ c.ocspResponse = encryptedExtensions.extensions.ocspResponse
+ c.sctList = encryptedExtensions.extensions.sctList
msg, err := c.readHandshake()
if err != nil {
diff --git a/ssl/test/runner/handshake_messages.go b/ssl/test/runner/handshake_messages.go
index d349ae7..baa07bd 100644
--- a/ssl/test/runner/handshake_messages.go
+++ b/ssl/test/runner/handshake_messages.go
@@ -738,7 +738,7 @@
extensions.addU16(0) // Length
}
} else {
- m.extensions.marshal(extensions)
+ m.extensions.marshal(extensions, m.vers)
if extensions.len() == 0 {
hello.discardChild()
}
@@ -837,7 +837,7 @@
return false
}
}
- } else if !m.extensions.unmarshal(data) {
+ } else if !m.extensions.unmarshal(data, m.vers) {
return false
}
@@ -858,7 +858,7 @@
encryptedExtensionsMsg.addU8(typeEncryptedExtensions)
encryptedExtensions := encryptedExtensionsMsg.addU24LengthPrefixed()
extensions := encryptedExtensions.addU16LengthPrefixed()
- m.extensions.marshal(extensions)
+ m.extensions.marshal(extensions, VersionTLS13)
m.raw = encryptedExtensionsMsg.finish()
return m.raw
@@ -881,13 +881,14 @@
if extLen != len(data) {
return false
}
- return m.extensions.unmarshal(data)
+ return m.extensions.unmarshal(data, VersionTLS13)
}
type serverExtensions struct {
nextProtoNeg bool
nextProtos []string
ocspStapling bool
+ ocspResponse []byte
ticketSupported bool
secureRenegotiation []byte
alpnProtocol string
@@ -902,7 +903,7 @@
npnLast bool
}
-func (m *serverExtensions) marshal(extensions *byteBuilder) {
+func (m *serverExtensions) marshal(extensions *byteBuilder, version uint16) {
if m.duplicateExtension {
// Add a duplicate bogus extension at the beginning and end.
extensions.addU16(0xffff)
@@ -920,9 +921,19 @@
npn.addBytes([]byte(v))
}
}
- if m.ocspStapling {
- extensions.addU16(extensionStatusRequest)
- extensions.addU16(0)
+ if version >= VersionTLS13 && enableTLS13Handshake {
+ if m.ocspResponse != nil {
+ extensions.addU16(extensionStatusRequest)
+ body := extensions.addU16LengthPrefixed()
+ body.addU8(statusTypeOCSP)
+ response := body.addU24LengthPrefixed()
+ response.addBytes(m.ocspResponse)
+ }
+ } else {
+ if m.ocspStapling {
+ extensions.addU16(extensionStatusRequest)
+ extensions.addU16(0)
+ }
}
if m.ticketSupported {
extensions.addU16(extensionSessionTicket)
@@ -989,7 +1000,7 @@
}
}
-func (m *serverExtensions) unmarshal(data []byte) bool {
+func (m *serverExtensions) unmarshal(data []byte, version uint16) bool {
// Reset all fields.
*m = serverExtensions{}
@@ -1018,10 +1029,25 @@
d = d[l:]
}
case extensionStatusRequest:
- if length > 0 {
- return false
+ if version >= VersionTLS13 && enableTLS13Handshake {
+ if length < 4 {
+ return false
+ }
+ d := data[:length]
+ if d[0] != statusTypeOCSP {
+ return false
+ }
+ respLen := int(d[1])<<16 | int(d[2])<<8 | int(d[3])
+ if respLen+4 != len(d) || respLen == 0 {
+ return false
+ }
+ m.ocspResponse = d[4:]
+ } else {
+ if length > 0 {
+ return false
+ }
+ m.ocspStapling = true
}
- m.ocspStapling = true
case extensionSessionTicket:
if length > 0 {
return false