TLSv1.3 -draft23: Implementation of signature_algorithms_cert

Tris uses signature_algorithms_cert in order to advertise that it
doesn't support RSA-PSS. See GH#86 for more detailed discussion.
This commit is contained in:
Henry Case 2018-06-26 14:25:45 +01:00 committed by Henry Dorsett Case
parent 5bdf1af124
commit 03138ec18e
5 changed files with 147 additions and 94 deletions

13
13.go
View File

@ -225,6 +225,7 @@ CurvePreferenceLoop:
certReq := new(certificateRequestMsg13) certReq := new(certificateRequestMsg13)
// extension 'signature_algorithms' MUST be specified // extension 'signature_algorithms' MUST be specified
certReq.supportedSignatureAlgorithms = supportedSignatureAlgorithms13 certReq.supportedSignatureAlgorithms = supportedSignatureAlgorithms13
certReq.supportedSignatureAlgorithmsCert = supportedSigAlgorithmsCert(supportedSignatureAlgorithms13)
hs.keySchedule.write(certReq.marshal()) hs.keySchedule.write(certReq.marshal())
if _, err := hs.c.writeRecord(recordTypeHandshake, certReq.marshal()); err != nil { if _, err := hs.c.writeRecord(recordTypeHandshake, certReq.marshal()); err != nil {
return err return err
@ -1114,3 +1115,15 @@ func (hs *clientHandshakeState) doTLS13Handshake() error {
c.in.setCipher(c.vers, appServerCipher) c.in.setCipher(c.vers, appServerCipher)
return nil return nil
} }
// supportedSigAlgorithmsCert iterates over schemes and filters out those algorithms
// which are not supported for certificate verification.
func supportedSigAlgorithmsCert(schemes []SignatureScheme) (ret []SignatureScheme) {
for _, sig := range schemes {
// X509 doesn't support PSS signatures
if !signatureSchemeIsPSS(sig) {
ret = append(ret, sig)
}
}
return
}

View File

@ -93,6 +93,7 @@ const (
extensionSupportedVersions uint16 = 43 extensionSupportedVersions uint16 = 43
extensionPSKKeyExchangeModes uint16 = 45 extensionPSKKeyExchangeModes uint16 = 45
extensionCAs uint16 = 47 extensionCAs uint16 = 47
extensionSignatureAlgorithmsCert uint16 = 50
extensionKeyShare uint16 = 51 extensionKeyShare uint16 = 51
extensionNextProtoNeg uint16 = 13172 // not IANA assigned extensionNextProtoNeg uint16 = 13172 // not IANA assigned
extensionRenegotiationInfo uint16 = 0xff01 extensionRenegotiationInfo uint16 = 0xff01

View File

@ -155,8 +155,8 @@ func ExampleConfig_keyLogWriter_TLS13() {
// preferences. // preferences.
// Output: // Output:
// CLIENT_HANDSHAKE_TRAFFIC_SECRET 0000000000000000000000000000000000000000000000000000000000000000 ab02b68658d18ef1a4056b3094fe511b43084d40e9a6518753a7f832da724292 // CLIENT_HANDSHAKE_TRAFFIC_SECRET 0000000000000000000000000000000000000000000000000000000000000000 a829dab06ccbe9323e0ad6cf331cd64d9a499f7f4b1e0b52d0dfaba90c07f275
// SERVER_HANDSHAKE_TRAFFIC_SECRET 0000000000000000000000000000000000000000000000000000000000000000 d2e96648d170e2524bee07b651f4cca932a52247493ca33cc0714260a7424b2d // SERVER_HANDSHAKE_TRAFFIC_SECRET 0000000000000000000000000000000000000000000000000000000000000000 5f6a288b5b5aba5cfc65d9966b279e911ac58bd7f81abbb67b10427106f01940
// SERVER_TRAFFIC_SECRET_0 0000000000000000000000000000000000000000000000000000000000000000 371fab23269e3cd73496e0e78f3dbc487f7cd5a563cc9f8c1a71be242268c375 // SERVER_TRAFFIC_SECRET_0 0000000000000000000000000000000000000000000000000000000000000000 1f202d1c1d43e74a8c4f46a56eae2cef9de417ff9f5f3927195eacc168f459b3
// CLIENT_TRAFFIC_SECRET_0 0000000000000000000000000000000000000000000000000000000000000000 ca30484e48ec9a6f3b05b41c7492dbed8dea8e92d2abece2824a96052ac8ed8d // CLIENT_TRAFFIC_SECRET_0 0000000000000000000000000000000000000000000000000000000000000000 67ece7874ec4f661fe1f06fb4240decd25f54062d78f0784844bf222d1967c20
} }

View File

@ -106,6 +106,7 @@ NextCipherSuite:
hello.vers = VersionTLS12 hello.vers = VersionTLS12
hello.supportedVersions = config.getSupportedVersions() hello.supportedVersions = config.getSupportedVersions()
hello.supportedSignatureAlgorithms = supportedSignatureAlgorithms13 hello.supportedSignatureAlgorithms = supportedSignatureAlgorithms13
hello.supportedSignatureAlgorithmsCert = supportedSigAlgorithmsCert(supportedSignatureAlgorithms13)
} }
return hello, nil return hello, nil

View File

@ -6,9 +6,23 @@ package tls
import ( import (
"bytes" "bytes"
"encoding/binary"
"strings" "strings"
) )
// signAlgosCertList helper function returns either list of signature algorithms in case
// signature_algorithms_cert extension should be marshalled or nil in the other case.
// signAlgos is a list of algorithms from signature_algorithms extension. signAlgosCert is a list
// of algorithms from signature_algorithms_cert extension.
func signAlgosCertList(signAlgos, signAlgosCert []SignatureScheme) []SignatureScheme {
if eqSignatureAlgorithms(signAlgos, signAlgosCert) {
// ensure that only supported_algorithms extension is send if supported_algorithms_cert
// has identical content
return nil
}
return signAlgosCert
}
type clientHelloMsg struct { type clientHelloMsg struct {
raw []byte raw []byte
rawTruncated []byte // for PSK binding rawTruncated []byte // for PSK binding
@ -26,6 +40,7 @@ type clientHelloMsg struct {
ticketSupported bool ticketSupported bool
sessionTicket []uint8 sessionTicket []uint8
supportedSignatureAlgorithms []SignatureScheme supportedSignatureAlgorithms []SignatureScheme
supportedSignatureAlgorithmsCert []SignatureScheme
secureRenegotiation []byte secureRenegotiation []byte
secureRenegotiationSupported bool secureRenegotiationSupported bool
alpnProtocols []string alpnProtocols []string
@ -37,50 +52,51 @@ type clientHelloMsg struct {
} }
// Helpers // Helpers
// Function used for signature_algorithms and signature_algorithrms_cert extensions only
// Marshalling of signature_algorithms extension see https://tools.ietf.org/html/rfc5246#section-7.4.1.4.1 // (for more details, see TLS 1.3 draft 28, 4.2.3)
// for more details. Extension is serialized in data buffer
// Function advances data slice and returns it, so that it can be used for further processing // Function advances data slice and returns it, so that it can be used for further processing
func marshalExtensionSignatureAlgorithms(data []byte, sigSchemes []SignatureScheme) []byte { func marshalExtensionSignatureAlgorithms(extension uint16, data []byte, schemes []SignatureScheme) []byte {
data[0] = byte(extensionSignatureAlgorithms >> 8) algNum := uint16(len(schemes))
data[1] = byte(extensionSignatureAlgorithms) if algNum == 0 {
l := 2 + 2*len(sigSchemes) return data
data[2] = byte(l >> 8) }
data[3] = byte(l)
data = data[4:]
l -= 2 binary.BigEndian.PutUint16(data, extension)
data[0] = byte(l >> 8)
data[1] = byte(l)
data = data[2:] data = data[2:]
for _, sigAlgo := range sigSchemes { binary.BigEndian.PutUint16(data, (2*algNum)+2) // +1 for length
data[0] = byte(sigAlgo >> 8) data = data[2:]
data[1] = byte(sigAlgo) binary.BigEndian.PutUint16(data, (2 * algNum))
data = data[2:]
for _, algo := range schemes {
binary.BigEndian.PutUint16(data, uint16(algo))
data = data[2:] data = data[2:]
} }
return data return data
} }
// Unmrshalling of signature_algorithms extension see https://tools.ietf.org/html/rfc5246#section-7.4.1.4.1 // Function used for unmarshalling signature_algorithms or signature_algorithms_cert extensions only
// for more details. // (for more details, see TLS 1.3 draft 28, 4.2.3)
// In case of error function returns alertDecoderError otherwise filled SignatureScheme slice and alertSuccess // In case of error function returns alertDecoderError otherwise filled SignatureScheme slice and alertSuccess
func unmarshalExtensionSignatureAlgorithms(data []byte, length int) ([]SignatureScheme, alert) { func unmarshalExtensionSignatureAlgorithms(data []byte, length int) ([]SignatureScheme, alert) {
if length < 2 || length&1 != 0 { if length < 2 || length&1 != 0 {
return nil, alertDecodeError return nil, alertDecodeError
} }
l := int(data[0])<<8 | int(data[1]) algLen := binary.BigEndian.Uint16(data)
if l != length-2 { idx := 2
if int(algLen) != length-2 {
return nil, alertDecodeError return nil, alertDecodeError
} }
n := l / 2
d := data[2:] schemes := make([]SignatureScheme, algLen/2)
sigSchemes := make([]SignatureScheme, n) for i := range schemes {
for i := range sigSchemes { schemes[i] = SignatureScheme(binary.BigEndian.Uint16(data[idx:]))
sigSchemes[i] = SignatureScheme(d[0])<<8 | SignatureScheme(d[1]) idx += 2
d = d[2:]
} }
return sigSchemes, alertSuccess return schemes, alertSuccess
} }
func (m *clientHelloMsg) equal(i interface{}) bool { func (m *clientHelloMsg) equal(i interface{}) bool {
@ -104,6 +120,7 @@ func (m *clientHelloMsg) equal(i interface{}) bool {
m.ticketSupported == m1.ticketSupported && m.ticketSupported == m1.ticketSupported &&
bytes.Equal(m.sessionTicket, m1.sessionTicket) && bytes.Equal(m.sessionTicket, m1.sessionTicket) &&
eqSignatureAlgorithms(m.supportedSignatureAlgorithms, m1.supportedSignatureAlgorithms) && eqSignatureAlgorithms(m.supportedSignatureAlgorithms, m1.supportedSignatureAlgorithms) &&
eqSignatureAlgorithms(m.supportedSignatureAlgorithmsCert, m1.supportedSignatureAlgorithmsCert) &&
m.secureRenegotiationSupported == m1.secureRenegotiationSupported && m.secureRenegotiationSupported == m1.secureRenegotiationSupported &&
bytes.Equal(m.secureRenegotiation, m1.secureRenegotiation) && bytes.Equal(m.secureRenegotiation, m1.secureRenegotiation) &&
eqStrings(m.alpnProtocols, m1.alpnProtocols) && eqStrings(m.alpnProtocols, m1.alpnProtocols) &&
@ -120,6 +137,8 @@ func (m *clientHelloMsg) marshal() []byte {
length := 2 + 32 + 1 + len(m.sessionId) + 2 + len(m.cipherSuites)*2 + 1 + len(m.compressionMethods) length := 2 + 32 + 1 + len(m.sessionId) + 2 + len(m.cipherSuites)*2 + 1 + len(m.compressionMethods)
numExtensions := 0 numExtensions := 0
extensionsLength := 0 extensionsLength := 0
// Indicates wether to send signature_algorithms_cert extension
if m.nextProtoNeg { if m.nextProtoNeg {
numExtensions++ numExtensions++
} }
@ -147,6 +166,10 @@ func (m *clientHelloMsg) marshal() []byte {
extensionsLength += 2 + 2*len(m.supportedSignatureAlgorithms) extensionsLength += 2 + 2*len(m.supportedSignatureAlgorithms)
numExtensions++ numExtensions++
} }
if m.getSignatureAlgorithmsCert() != nil {
extensionsLength += 2 + 2*len(m.getSignatureAlgorithmsCert())
numExtensions++
}
if m.secureRenegotiationSupported { if m.secureRenegotiationSupported {
extensionsLength += 1 + len(m.secureRenegotiation) extensionsLength += 1 + len(m.secureRenegotiation)
numExtensions++ numExtensions++
@ -305,26 +328,15 @@ func (m *clientHelloMsg) marshal() []byte {
copy(z, m.sessionTicket) copy(z, m.sessionTicket)
z = z[len(m.sessionTicket):] z = z[len(m.sessionTicket):]
} }
if len(m.supportedSignatureAlgorithms) > 0 {
// https://tools.ietf.org/html/rfc5246#section-7.4.1.4.1
// https://tools.ietf.org/html/draft-ietf-tls-tls13-18#section-4.2.3
z[0] = byte(extensionSignatureAlgorithms >> 8)
z[1] = byte(extensionSignatureAlgorithms)
l := 2 + 2*len(m.supportedSignatureAlgorithms)
z[2] = byte(l >> 8)
z[3] = byte(l)
z = z[4:]
l -= 2 if len(m.supportedSignatureAlgorithms) > 0 {
z[0] = byte(l >> 8) z = marshalExtensionSignatureAlgorithms(extensionSignatureAlgorithms, z, m.supportedSignatureAlgorithms)
z[1] = byte(l)
z = z[2:]
for _, sigAlgo := range m.supportedSignatureAlgorithms {
z[0] = byte(sigAlgo >> 8)
z[1] = byte(sigAlgo)
z = z[2:]
} }
if m.getSignatureAlgorithmsCert() != nil {
// Ensure only one list of algorithms is sent if supported_algorithms and supported_algorithms_cert are the same
z = marshalExtensionSignatureAlgorithms(extensionSignatureAlgorithmsCert, z, m.getSignatureAlgorithmsCert())
} }
if m.secureRenegotiationSupported { if m.secureRenegotiationSupported {
z[0] = byte(extensionRenegotiationInfo >> 8) z[0] = byte(extensionRenegotiationInfo >> 8)
z[1] = byte(extensionRenegotiationInfo & 0xff) z[1] = byte(extensionRenegotiationInfo & 0xff)
@ -743,6 +755,10 @@ func (m *clientHelloMsg) unmarshal(data []byte) alert {
return alertSuccess return alertSuccess
} }
func (m *clientHelloMsg) getSignatureAlgorithmsCert() []SignatureScheme {
return signAlgosCertList(m.supportedSignatureAlgorithms, m.supportedSignatureAlgorithmsCert)
}
type serverHelloMsg struct { type serverHelloMsg struct {
raw []byte raw []byte
vers uint16 vers uint16
@ -2074,6 +2090,7 @@ func (m *certificateRequestMsg) unmarshal(data []byte) alert {
if len(data) != 0 { if len(data) != 0 {
return alertDecodeError return alertDecodeError
} }
return alertSuccess return alertSuccess
} }
@ -2082,6 +2099,7 @@ type certificateRequestMsg13 struct {
requestContext []byte requestContext []byte
supportedSignatureAlgorithms []SignatureScheme supportedSignatureAlgorithms []SignatureScheme
supportedSignatureAlgorithmsCert []SignatureScheme
certificateAuthorities [][]byte certificateAuthorities [][]byte
} }
@ -2091,7 +2109,8 @@ func (m *certificateRequestMsg13) equal(i interface{}) bool {
bytes.Equal(m.raw, m1.raw) && bytes.Equal(m.raw, m1.raw) &&
bytes.Equal(m.requestContext, m1.requestContext) && bytes.Equal(m.requestContext, m1.requestContext) &&
eqByteSlices(m.certificateAuthorities, m1.certificateAuthorities) && eqByteSlices(m.certificateAuthorities, m1.certificateAuthorities) &&
eqSignatureAlgorithms(m.supportedSignatureAlgorithms, m1.supportedSignatureAlgorithms) eqSignatureAlgorithms(m.supportedSignatureAlgorithms, m1.supportedSignatureAlgorithms) &&
eqSignatureAlgorithms(m.supportedSignatureAlgorithmsCert, m1.supportedSignatureAlgorithmsCert)
} }
func (m *certificateRequestMsg13) marshal() (x []byte) { func (m *certificateRequestMsg13) marshal() (x []byte) {
@ -2104,6 +2123,11 @@ func (m *certificateRequestMsg13) marshal() (x []byte) {
numExtensions := 1 numExtensions := 1
extensionsLength := 2 + 2*len(m.supportedSignatureAlgorithms) extensionsLength := 2 + 2*len(m.supportedSignatureAlgorithms)
if m.getSignatureAlgorithmsCert() != nil {
numExtensions += 1
extensionsLength += 2 + 2*len(m.getSignatureAlgorithmsCert())
}
casLength := 0 casLength := 0
if len(m.certificateAuthorities) > 0 { if len(m.certificateAuthorities) > 0 {
for _, ca := range m.certificateAuthorities { for _, ca := range m.certificateAuthorities {
@ -2131,7 +2155,11 @@ func (m *certificateRequestMsg13) marshal() (x []byte) {
z = z[2:] z = z[2:]
// TODO: this function should be reused by CH // TODO: this function should be reused by CH
z = marshalExtensionSignatureAlgorithms(z, m.supportedSignatureAlgorithms) z = marshalExtensionSignatureAlgorithms(extensionSignatureAlgorithms, z, m.supportedSignatureAlgorithms)
if m.getSignatureAlgorithmsCert() != nil {
z = marshalExtensionSignatureAlgorithms(extensionSignatureAlgorithmsCert, z, m.getSignatureAlgorithmsCert())
}
// certificate_authorities // certificate_authorities
if casLength > 0 { if casLength > 0 {
z[0] = byte(extensionCAs >> 8) z[0] = byte(extensionCAs >> 8)
@ -2204,6 +2232,12 @@ func (m *certificateRequestMsg13) unmarshal(data []byte) alert {
if err != alertSuccess { if err != alertSuccess {
return err return err
} }
case extensionSignatureAlgorithmsCert:
var err alert
m.supportedSignatureAlgorithmsCert, err = unmarshalExtensionSignatureAlgorithms(data, length)
if err != alertSuccess {
return err
}
case extensionCAs: case extensionCAs:
// TODO DRY: share code with CH // TODO DRY: share code with CH
if length < 2 { if length < 2 {
@ -2240,6 +2274,10 @@ func (m *certificateRequestMsg13) unmarshal(data []byte) alert {
return alertSuccess return alertSuccess
} }
func (m *certificateRequestMsg13) getSignatureAlgorithmsCert() []SignatureScheme {
return signAlgosCertList(m.supportedSignatureAlgorithms, m.supportedSignatureAlgorithmsCert)
}
type certificateVerifyMsg struct { type certificateVerifyMsg struct {
raw []byte raw []byte
hasSignatureAndHash bool hasSignatureAndHash bool