mirror of
https://github.com/refraction-networking/utls.git
synced 2025-04-03 20:17:36 +03:00
crypto/tls: implement TLS 1.3 version-specific messages
Note that there is significant code duplication due to extensions with the same format appearing in different messages in TLS 1.3. This will be cleaned up in a future refactor once CL 145317 is merged. Enforcing the presence/absence of each extension in each message is left to the upper layer, based on both protocol version and extensions advertised in CH and CR. Duplicated extensions and unknown extensions in SH, EE, HRR, and CT will be tightened up in a future CL. The TLS 1.2 CertificateStatus message was restricted to accepting only type OCSP as any other type (none of which are specified so far) would have to be negotiated. Updates #9671 Change-Id: I7c42394c5cc0af01faa84b9b9f25fdc6e7cfbb9e Reviewed-on: https://go-review.googlesource.com/c/145477 Reviewed-by: Adam Langley <agl@golang.org>
This commit is contained in:
parent
3fdae13d6a
commit
d4e9432552
6 changed files with 613 additions and 65 deletions
|
@ -71,6 +71,7 @@ type clientHelloMsg struct {
|
|||
supportedVersions []uint16
|
||||
cookie []byte
|
||||
keyShares []keyShare
|
||||
earlyData bool
|
||||
pskModes []uint8
|
||||
pskIdentities []pskIdentity
|
||||
pskBinders [][]byte
|
||||
|
@ -239,6 +240,11 @@ func (m *clientHelloMsg) marshal() []byte {
|
|||
})
|
||||
})
|
||||
}
|
||||
if m.earlyData {
|
||||
// RFC 8446, Section 4.2.10
|
||||
b.AddUint16(extensionEarlyData)
|
||||
b.AddUint16(0) // empty extension_data
|
||||
}
|
||||
if len(m.pskModes) > 0 {
|
||||
// RFC 8446, Section 4.2.9
|
||||
b.AddUint16(extensionPSKModes)
|
||||
|
@ -478,6 +484,9 @@ func (m *clientHelloMsg) unmarshal(data []byte) bool {
|
|||
}
|
||||
m.keyShares = append(m.keyShares, ks)
|
||||
}
|
||||
case extensionEarlyData:
|
||||
// RFC 8446, Section 4.2.10
|
||||
m.earlyData = true
|
||||
case extensionPSKModes:
|
||||
// RFC 8446, Section 4.2.9
|
||||
if !readUint8LengthPrefixed(&extData, &m.pskModes) {
|
||||
|
@ -782,6 +791,342 @@ func (m *serverHelloMsg) unmarshal(data []byte) bool {
|
|||
return true
|
||||
}
|
||||
|
||||
type encryptedExtensionsMsg struct {
|
||||
raw []byte
|
||||
alpnProtocol string
|
||||
}
|
||||
|
||||
func (m *encryptedExtensionsMsg) marshal() []byte {
|
||||
if m.raw != nil {
|
||||
return m.raw
|
||||
}
|
||||
|
||||
var b cryptobyte.Builder
|
||||
b.AddUint8(typeEncryptedExtensions)
|
||||
b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) {
|
||||
b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
|
||||
if len(m.alpnProtocol) > 0 {
|
||||
b.AddUint16(extensionALPN)
|
||||
b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
|
||||
b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
|
||||
b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) {
|
||||
b.AddBytes([]byte(m.alpnProtocol))
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
m.raw = b.BytesOrPanic()
|
||||
return m.raw
|
||||
}
|
||||
|
||||
func (m *encryptedExtensionsMsg) unmarshal(data []byte) bool {
|
||||
*m = encryptedExtensionsMsg{raw: data}
|
||||
s := cryptobyte.String(data)
|
||||
|
||||
var extensions cryptobyte.String
|
||||
if !s.Skip(4) || // message type and uint24 length field
|
||||
!s.ReadUint16LengthPrefixed(&extensions) || !s.Empty() {
|
||||
return false
|
||||
}
|
||||
|
||||
for !extensions.Empty() {
|
||||
var extension uint16
|
||||
var extData cryptobyte.String
|
||||
if !extensions.ReadUint16(&extension) ||
|
||||
!extensions.ReadUint16LengthPrefixed(&extData) {
|
||||
return false
|
||||
}
|
||||
|
||||
switch extension {
|
||||
case extensionALPN:
|
||||
var protoList cryptobyte.String
|
||||
if !extData.ReadUint16LengthPrefixed(&protoList) || protoList.Empty() {
|
||||
return false
|
||||
}
|
||||
var proto cryptobyte.String
|
||||
if !protoList.ReadUint8LengthPrefixed(&proto) ||
|
||||
proto.Empty() || !protoList.Empty() {
|
||||
return false
|
||||
}
|
||||
m.alpnProtocol = string(proto)
|
||||
default:
|
||||
// Ignore unknown extensions.
|
||||
continue
|
||||
}
|
||||
|
||||
if !extData.Empty() {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
type endOfEarlyDataMsg struct{}
|
||||
|
||||
func (m *endOfEarlyDataMsg) marshal() []byte {
|
||||
x := make([]byte, 4)
|
||||
x[0] = typeEndOfEarlyData
|
||||
return x
|
||||
}
|
||||
|
||||
func (m *endOfEarlyDataMsg) unmarshal(data []byte) bool {
|
||||
return len(data) == 4
|
||||
}
|
||||
|
||||
type keyUpdateMsg struct {
|
||||
raw []byte
|
||||
updateRequested bool
|
||||
}
|
||||
|
||||
func (m *keyUpdateMsg) marshal() []byte {
|
||||
if m.raw != nil {
|
||||
return m.raw
|
||||
}
|
||||
|
||||
var b cryptobyte.Builder
|
||||
b.AddUint8(typeKeyUpdate)
|
||||
b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) {
|
||||
if m.updateRequested {
|
||||
b.AddUint8(1)
|
||||
} else {
|
||||
b.AddUint8(0)
|
||||
}
|
||||
})
|
||||
|
||||
m.raw = b.BytesOrPanic()
|
||||
return m.raw
|
||||
}
|
||||
|
||||
func (m *keyUpdateMsg) unmarshal(data []byte) bool {
|
||||
m.raw = data
|
||||
s := cryptobyte.String(data)
|
||||
|
||||
var updateRequested uint8
|
||||
if !s.Skip(4) || // message type and uint24 length field
|
||||
!s.ReadUint8(&updateRequested) || !s.Empty() {
|
||||
return false
|
||||
}
|
||||
switch updateRequested {
|
||||
case 0:
|
||||
m.updateRequested = false
|
||||
case 1:
|
||||
m.updateRequested = true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
type newSessionTicketMsgTLS13 struct {
|
||||
raw []byte
|
||||
lifetime uint32
|
||||
ageAdd uint32
|
||||
nonce []byte
|
||||
label []byte
|
||||
maxEarlyData uint32
|
||||
}
|
||||
|
||||
func (m *newSessionTicketMsgTLS13) marshal() []byte {
|
||||
if m.raw != nil {
|
||||
return m.raw
|
||||
}
|
||||
|
||||
var b cryptobyte.Builder
|
||||
b.AddUint8(typeNewSessionTicket)
|
||||
b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) {
|
||||
b.AddUint32(m.lifetime)
|
||||
b.AddUint32(m.ageAdd)
|
||||
b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) {
|
||||
b.AddBytes(m.nonce)
|
||||
})
|
||||
b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
|
||||
b.AddBytes(m.label)
|
||||
})
|
||||
|
||||
b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
|
||||
if m.maxEarlyData > 0 {
|
||||
b.AddUint16(extensionEarlyData)
|
||||
b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
|
||||
b.AddUint32(m.maxEarlyData)
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
m.raw = b.BytesOrPanic()
|
||||
return m.raw
|
||||
}
|
||||
|
||||
func (m *newSessionTicketMsgTLS13) unmarshal(data []byte) bool {
|
||||
*m = newSessionTicketMsgTLS13{raw: data}
|
||||
s := cryptobyte.String(data)
|
||||
|
||||
var extensions cryptobyte.String
|
||||
if !s.Skip(4) || // message type and uint24 length field
|
||||
!s.ReadUint32(&m.lifetime) ||
|
||||
!s.ReadUint32(&m.ageAdd) ||
|
||||
!readUint8LengthPrefixed(&s, &m.nonce) ||
|
||||
!readUint16LengthPrefixed(&s, &m.label) ||
|
||||
!s.ReadUint16LengthPrefixed(&extensions) ||
|
||||
!s.Empty() {
|
||||
return false
|
||||
}
|
||||
|
||||
for !extensions.Empty() {
|
||||
var extension uint16
|
||||
var extData cryptobyte.String
|
||||
if !extensions.ReadUint16(&extension) ||
|
||||
!extensions.ReadUint16LengthPrefixed(&extData) {
|
||||
return false
|
||||
}
|
||||
|
||||
switch extension {
|
||||
case extensionEarlyData:
|
||||
if !extData.ReadUint32(&m.maxEarlyData) {
|
||||
return false
|
||||
}
|
||||
default:
|
||||
// Ignore unknown extensions.
|
||||
continue
|
||||
}
|
||||
|
||||
if !extData.Empty() {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
type certificateRequestMsgTLS13 struct {
|
||||
raw []byte
|
||||
ocspStapling bool
|
||||
scts bool
|
||||
supportedSignatureAlgorithms []SignatureScheme
|
||||
supportedSignatureAlgorithmsCert []SignatureScheme
|
||||
}
|
||||
|
||||
func (m *certificateRequestMsgTLS13) marshal() []byte {
|
||||
if m.raw != nil {
|
||||
return m.raw
|
||||
}
|
||||
|
||||
var b cryptobyte.Builder
|
||||
b.AddUint8(typeCertificateRequest)
|
||||
b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) {
|
||||
// certificate_request_context (SHALL be zero length unless used for
|
||||
// post-handshake authentication)
|
||||
b.AddUint8(0)
|
||||
|
||||
b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
|
||||
if m.ocspStapling {
|
||||
b.AddUint16(extensionStatusRequest)
|
||||
b.AddUint16(0) // empty extension_data
|
||||
}
|
||||
if m.scts {
|
||||
// RFC 8446, Section 4.4.2.1 makes no mention of
|
||||
// signed_certificate_timestamp in CertificateRequest, but
|
||||
// "Extensions in the Certificate message from the client MUST
|
||||
// correspond to extensions in the CertificateRequest message
|
||||
// from the server." and it appears in the table in Section 4.2.
|
||||
b.AddUint16(extensionSCT)
|
||||
b.AddUint16(0) // empty extension_data
|
||||
}
|
||||
if len(m.supportedSignatureAlgorithms) > 0 {
|
||||
b.AddUint16(extensionSignatureAlgorithms)
|
||||
b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
|
||||
b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
|
||||
for _, sigAlgo := range m.supportedSignatureAlgorithms {
|
||||
b.AddUint16(uint16(sigAlgo))
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
if len(m.supportedSignatureAlgorithmsCert) > 0 {
|
||||
b.AddUint16(extensionSignatureAlgorithmsCert)
|
||||
b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
|
||||
b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
|
||||
for _, sigAlgo := range m.supportedSignatureAlgorithmsCert {
|
||||
b.AddUint16(uint16(sigAlgo))
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
m.raw = b.BytesOrPanic()
|
||||
return m.raw
|
||||
}
|
||||
|
||||
func (m *certificateRequestMsgTLS13) unmarshal(data []byte) bool {
|
||||
*m = certificateRequestMsgTLS13{raw: data}
|
||||
s := cryptobyte.String(data)
|
||||
|
||||
var context, extensions cryptobyte.String
|
||||
if !s.Skip(4) || // message type and uint24 length field
|
||||
!s.ReadUint8LengthPrefixed(&context) || !context.Empty() ||
|
||||
!s.ReadUint16LengthPrefixed(&extensions) ||
|
||||
!s.Empty() {
|
||||
return false
|
||||
}
|
||||
|
||||
for !extensions.Empty() {
|
||||
var extension uint16
|
||||
var extData cryptobyte.String
|
||||
if !extensions.ReadUint16(&extension) ||
|
||||
!extensions.ReadUint16LengthPrefixed(&extData) {
|
||||
return false
|
||||
}
|
||||
|
||||
switch extension {
|
||||
case extensionStatusRequest:
|
||||
m.ocspStapling = true
|
||||
case extensionSCT:
|
||||
m.scts = true
|
||||
case extensionSignatureAlgorithms:
|
||||
var sigAndAlgs cryptobyte.String
|
||||
if !extData.ReadUint16LengthPrefixed(&sigAndAlgs) || sigAndAlgs.Empty() {
|
||||
return false
|
||||
}
|
||||
for !sigAndAlgs.Empty() {
|
||||
var sigAndAlg uint16
|
||||
if !sigAndAlgs.ReadUint16(&sigAndAlg) {
|
||||
return false
|
||||
}
|
||||
m.supportedSignatureAlgorithms = append(
|
||||
m.supportedSignatureAlgorithms, SignatureScheme(sigAndAlg))
|
||||
}
|
||||
case extensionSignatureAlgorithmsCert:
|
||||
var sigAndAlgs cryptobyte.String
|
||||
if !extData.ReadUint16LengthPrefixed(&sigAndAlgs) || sigAndAlgs.Empty() {
|
||||
return false
|
||||
}
|
||||
for !sigAndAlgs.Empty() {
|
||||
var sigAndAlg uint16
|
||||
if !sigAndAlgs.ReadUint16(&sigAndAlg) {
|
||||
return false
|
||||
}
|
||||
m.supportedSignatureAlgorithmsCert = append(
|
||||
m.supportedSignatureAlgorithmsCert, SignatureScheme(sigAndAlg))
|
||||
}
|
||||
default:
|
||||
// Ignore unknown extensions.
|
||||
continue
|
||||
}
|
||||
|
||||
if !extData.Empty() {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
type certificateMsg struct {
|
||||
raw []byte
|
||||
certificates [][]byte
|
||||
|
@ -859,6 +1204,131 @@ func (m *certificateMsg) unmarshal(data []byte) bool {
|
|||
return true
|
||||
}
|
||||
|
||||
type certificateMsgTLS13 struct {
|
||||
raw []byte
|
||||
certificate Certificate
|
||||
ocspStapling bool
|
||||
scts bool
|
||||
}
|
||||
|
||||
func (m *certificateMsgTLS13) marshal() []byte {
|
||||
if m.raw != nil {
|
||||
return m.raw
|
||||
}
|
||||
|
||||
var b cryptobyte.Builder
|
||||
b.AddUint8(typeCertificate)
|
||||
b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) {
|
||||
b.AddUint8(0) // certificate_request_context
|
||||
b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) {
|
||||
for i, cert := range m.certificate.Certificate {
|
||||
b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) {
|
||||
b.AddBytes(cert)
|
||||
})
|
||||
b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
|
||||
if i > 0 {
|
||||
// This library only supports OCSP and SCT for leaf certificates.
|
||||
return
|
||||
}
|
||||
if m.ocspStapling {
|
||||
b.AddUint16(extensionStatusRequest)
|
||||
b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
|
||||
b.AddUint8(statusTypeOCSP)
|
||||
b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) {
|
||||
b.AddBytes(m.certificate.OCSPStaple)
|
||||
})
|
||||
})
|
||||
}
|
||||
if m.scts {
|
||||
b.AddUint16(extensionSCT)
|
||||
b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
|
||||
b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
|
||||
for _, sct := range m.certificate.SignedCertificateTimestamps {
|
||||
b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
|
||||
b.AddBytes(sct)
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
m.raw = b.BytesOrPanic()
|
||||
return m.raw
|
||||
}
|
||||
|
||||
func (m *certificateMsgTLS13) unmarshal(data []byte) bool {
|
||||
*m = certificateMsgTLS13{raw: data}
|
||||
s := cryptobyte.String(data)
|
||||
|
||||
var context, certList cryptobyte.String
|
||||
if !s.Skip(4) || // message type and uint24 length field
|
||||
!s.ReadUint8LengthPrefixed(&context) || !context.Empty() ||
|
||||
!s.ReadUint24LengthPrefixed(&certList) ||
|
||||
!s.Empty() {
|
||||
return false
|
||||
}
|
||||
|
||||
for !certList.Empty() {
|
||||
var cert []byte
|
||||
var extensions cryptobyte.String
|
||||
if !readUint24LengthPrefixed(&certList, &cert) ||
|
||||
!certList.ReadUint16LengthPrefixed(&extensions) {
|
||||
return false
|
||||
}
|
||||
m.certificate.Certificate = append(m.certificate.Certificate, cert)
|
||||
for !extensions.Empty() {
|
||||
var extension uint16
|
||||
var extData cryptobyte.String
|
||||
if !extensions.ReadUint16(&extension) ||
|
||||
!extensions.ReadUint16LengthPrefixed(&extData) {
|
||||
return false
|
||||
}
|
||||
if len(m.certificate.Certificate) > 1 {
|
||||
// This library only supports OCSP and SCT for leaf certificates.
|
||||
continue
|
||||
}
|
||||
|
||||
switch extension {
|
||||
case extensionStatusRequest:
|
||||
m.ocspStapling = true
|
||||
var statusType uint8
|
||||
if !extData.ReadUint8(&statusType) || statusType != statusTypeOCSP ||
|
||||
!readUint24LengthPrefixed(&extData, &m.certificate.OCSPStaple) ||
|
||||
len(m.certificate.OCSPStaple) == 0 {
|
||||
return false
|
||||
}
|
||||
case extensionSCT:
|
||||
m.scts = true
|
||||
var sctList cryptobyte.String
|
||||
if !extData.ReadUint16LengthPrefixed(&sctList) || sctList.Empty() {
|
||||
return false
|
||||
}
|
||||
for !sctList.Empty() {
|
||||
var sct []byte
|
||||
if !readUint16LengthPrefixed(&sctList, &sct) ||
|
||||
len(sct) == 0 {
|
||||
return false
|
||||
}
|
||||
m.certificate.SignedCertificateTimestamps = append(
|
||||
m.certificate.SignedCertificateTimestamps, sct)
|
||||
}
|
||||
default:
|
||||
// Ignore unknown extensions.
|
||||
continue
|
||||
}
|
||||
|
||||
if !extData.Empty() {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
type serverKeyExchangeMsg struct {
|
||||
raw []byte
|
||||
key []byte
|
||||
|
@ -890,9 +1360,8 @@ func (m *serverKeyExchangeMsg) unmarshal(data []byte) bool {
|
|||
}
|
||||
|
||||
type certificateStatusMsg struct {
|
||||
raw []byte
|
||||
statusType uint8
|
||||
response []byte
|
||||
raw []byte
|
||||
response []byte
|
||||
}
|
||||
|
||||
func (m *certificateStatusMsg) marshal() []byte {
|
||||
|
@ -900,46 +1369,29 @@ func (m *certificateStatusMsg) marshal() []byte {
|
|||
return m.raw
|
||||
}
|
||||
|
||||
var x []byte
|
||||
if m.statusType == statusTypeOCSP {
|
||||
x = make([]byte, 4+4+len(m.response))
|
||||
x[0] = typeCertificateStatus
|
||||
l := len(m.response) + 4
|
||||
x[1] = byte(l >> 16)
|
||||
x[2] = byte(l >> 8)
|
||||
x[3] = byte(l)
|
||||
x[4] = statusTypeOCSP
|
||||
var b cryptobyte.Builder
|
||||
b.AddUint8(typeCertificateStatus)
|
||||
b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) {
|
||||
b.AddUint8(statusTypeOCSP)
|
||||
b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) {
|
||||
b.AddBytes(m.response)
|
||||
})
|
||||
})
|
||||
|
||||
l -= 4
|
||||
x[5] = byte(l >> 16)
|
||||
x[6] = byte(l >> 8)
|
||||
x[7] = byte(l)
|
||||
copy(x[8:], m.response)
|
||||
} else {
|
||||
x = []byte{typeCertificateStatus, 0, 0, 1, m.statusType}
|
||||
}
|
||||
|
||||
m.raw = x
|
||||
return x
|
||||
m.raw = b.BytesOrPanic()
|
||||
return m.raw
|
||||
}
|
||||
|
||||
func (m *certificateStatusMsg) unmarshal(data []byte) bool {
|
||||
m.raw = data
|
||||
if len(data) < 5 {
|
||||
return false
|
||||
}
|
||||
m.statusType = data[4]
|
||||
s := cryptobyte.String(data)
|
||||
|
||||
m.response = nil
|
||||
if m.statusType == statusTypeOCSP {
|
||||
if len(data) < 8 {
|
||||
return false
|
||||
}
|
||||
respLen := uint32(data[5])<<16 | uint32(data[6])<<8 | uint32(data[7])
|
||||
if uint32(len(data)) != 4+4+respLen {
|
||||
return false
|
||||
}
|
||||
m.response = data[8:]
|
||||
var statusType uint8
|
||||
if !s.Skip(4) || // message type and uint24 length field
|
||||
!s.ReadUint8(&statusType) || statusType != statusTypeOCSP ||
|
||||
!readUint24LengthPrefixed(&s, &m.response) ||
|
||||
len(m.response) == 0 || !s.Empty() {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue