From e62ddc004cd4909e51cc2a3111147fc366483d12 Mon Sep 17 00:00:00 2001 From: "Henry D. Case" Date: Mon, 26 Mar 2018 16:13:03 +0100 Subject: [PATCH] Adds structure for certificate_request in TLS 1.3 * TLS 1.3 requries specific marshalling/unmarshalling of data * This code should probably be rewritten in order ot use a bit cleaner approach for dealing with bytes --- handshake_messages.go | 210 +++++++++++++++++++++++++++++++++++++ handshake_messages_test.go | 13 +++ 2 files changed, 223 insertions(+) diff --git a/handshake_messages.go b/handshake_messages.go index 77b1b67..3362dd7 100644 --- a/handshake_messages.go +++ b/handshake_messages.go @@ -36,6 +36,53 @@ type clientHelloMsg struct { earlyData bool } +// Helpers + +// Marshalling of signature_algorithms extension see https://tools.ietf.org/html/rfc5246#section-7.4.1.4.1 +// 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 +func marshallExtensionSignatureAlgorithms(data []byte, sigSchemes []SignatureScheme) ([]byte) { + data[0] = byte(extensionSignatureAlgorithms >> 8) + data[1] = byte(extensionSignatureAlgorithms) + l := 2 + 2*len(sigSchemes) + data[2] = byte(l >> 8) + data[3] = byte(l) + data = data[4:] + + l -= 2 + data[0] = byte(l >> 8) + data[1] = byte(l) + data = data[2:] + for _, sigAlgo := range sigSchemes { + data[0] = byte(sigAlgo >> 8) + data[1] = byte(sigAlgo) + data = data[2:] + } + return data +} + +// Unmrshalling of signature_algorithms extension see https://tools.ietf.org/html/rfc5246#section-7.4.1.4.1 +// for more details. +// In case of error function returns alertDecoderError otherwise filled SignatureScheme slice and alertSuccess +func unmarshallExtensionSignatureAlgorithms(data []byte, length int) ([]SignatureScheme, alert) { + if length < 2 || length&1 != 0 { + return nil, alertDecodeError + } + + l := int(data[0])<<8 | int(data[1]) + if l != length-2 { + return nil, alertDecodeError + } + n := l / 2 + d := data[2:] + sigSchemes := make([]SignatureScheme, n) + for i := range sigSchemes { + sigSchemes[i] = SignatureScheme(d[0])<<8 | SignatureScheme(d[1]) + d = d[2:] + } + return sigSchemes, alertSuccess +} + func (m *clientHelloMsg) equal(i interface{}) bool { m1, ok := i.(*clientHelloMsg) if !ok { @@ -2030,6 +2077,169 @@ func (m *certificateRequestMsg) unmarshal(data []byte) alert { return alertSuccess } +type certificateRequestMsg13 struct { + raw []byte + + requestContext []byte + supportedSignatureAlgorithms []SignatureScheme + certificateAuthorities [][]byte +} + +func (m *certificateRequestMsg13) equal(i interface{}) bool { + m1, ok := i.(*certificateRequestMsg13) + return ok && + bytes.Equal(m.raw, m1.raw) && + bytes.Equal(m.requestContext, m1.requestContext) && + eqByteSlices(m.certificateAuthorities, m1.certificateAuthorities) && + eqSignatureAlgorithms(m.supportedSignatureAlgorithms, m1.supportedSignatureAlgorithms) +} + +func (m *certificateRequestMsg13) marshal() (x []byte) { + if m.raw != nil { + return m.raw + } + + // See https://tools.ietf.org/html/draft-ietf-tls-tls13-21#section-4.3.2 + length := 1 + len(m.requestContext) + numExtensions := 1 + extensionsLength := 2 + 2*len(m.supportedSignatureAlgorithms) + + casLength := 0 + if len(m.certificateAuthorities) > 0 { + for _, ca := range m.certificateAuthorities { + casLength += 2 + len(ca) + } + extensionsLength += 2 + casLength + numExtensions++ + } + + extensionsLength += 4 * numExtensions + length += 2 + extensionsLength + + x = make([]byte, 4+length) + x[0] = typeCertificateRequest + x[1] = uint8(length >> 16) + x[2] = uint8(length >> 8) + x[3] = uint8(length) + + x[4] = uint8(len(m.requestContext)) + copy(x[5:], m.requestContext) + z := x[5+len(m.requestContext):] + + z[0] = byte(extensionsLength >> 8) + z[1] = byte(extensionsLength) + z = z[2:] + + // TODO: this function should be reused by CH + z = marshallExtensionSignatureAlgorithms(z, m.supportedSignatureAlgorithms) + // certificate_authorities + if casLength > 0 { + z[0] = byte(extensionCAs >> 8) + z[1] = byte(extensionCAs) + l := 2 + casLength + z[2] = byte(l >> 8) + z[3] = byte(l) + z = z[4:] + + z[0] = uint8(casLength >> 8) + z[1] = uint8(casLength) + z = z[2:] + for _, ca := range m.certificateAuthorities { + z[0] = uint8(len(ca) >> 8) + z[1] = uint8(len(ca)) + z = z[2:] + copy(z, ca) + z = z[len(ca):] + } + } + + m.raw = x + return +} + +func (m *certificateRequestMsg13) unmarshal(data []byte) alert { + m.raw = data + m.supportedSignatureAlgorithms = nil + m.certificateAuthorities = nil + + if len(data) < 5 { + return alertDecodeError + } + + length := uint32(data[1])<<16 | uint32(data[2])<<8 | uint32(data[3]) + if uint32(len(data))-4 != length { + return alertDecodeError + } + + ctxLen := data[4] + if len(data) < 5+int(ctxLen)+2 { + return alertDecodeError + } + m.requestContext = data[5 : 5+ctxLen] + data = data[5+ctxLen:] + + extensionsLength := int(data[0])<<8 | int(data[1]) + data = data[2:] + if len(data) != extensionsLength { + return alertDecodeError + } + + for len(data) != 0 { + if len(data) < 4 { + return alertDecodeError + } + extension := uint16(data[0])<<8 | uint16(data[1]) + length := int(data[2])<<8 | int(data[3]) + data = data[4:] + if len(data) < length { + return alertDecodeError + } + + switch extension { + case extensionSignatureAlgorithms: + // TODO: unmarshallExtensionSignatureAlgorithms should be shared with CH and pre-1.3 CV + // https://tools.ietf.org/html/draft-ietf-tls-tls13-21#section-4.2.3 + var err alert + m.supportedSignatureAlgorithms, err = unmarshallExtensionSignatureAlgorithms(data, length) + if err != alertSuccess { + return err + } + case extensionCAs: + // TODO DRY: share code with CH + if length < 2 { + return alertDecodeError + } + l := int(data[0])<<8 | int(data[1]) + if l != length-2 || l < 3 { + return alertDecodeError + } + cas := make([]byte, l) + copy(cas, data[2:]) + m.certificateAuthorities = nil + for len(cas) > 0 { + if len(cas) < 2 { + return alertDecodeError + } + caLen := uint16(cas[0])<<8 | uint16(cas[1]) + cas = cas[2:] + + if len(cas) < int(caLen) { + return alertDecodeError + } + + m.certificateAuthorities = append(m.certificateAuthorities, cas[:caLen]) + cas = cas[caLen:] + } + } + data = data[length:] + } + + if len(m.supportedSignatureAlgorithms) == 0 { + return alertDecodeError + } + return alertSuccess +} + type certificateVerifyMsg struct { raw []byte hasSignatureAndHash bool diff --git a/handshake_messages_test.go b/handshake_messages_test.go index 79d8119..9ab291c 100644 --- a/handshake_messages_test.go +++ b/handshake_messages_test.go @@ -20,6 +20,7 @@ var tests = []interface{}{ &certificateMsg{}, &certificateRequestMsg{}, + &certificateRequestMsg13{}, &certificateVerifyMsg{}, &certificateStatusMsg{}, &clientKeyExchangeMsg{}, @@ -272,6 +273,18 @@ func (*certificateRequestMsg) Generate(rand *rand.Rand, size int) reflect.Value return reflect.ValueOf(m) } +func (*certificateRequestMsg13) Generate(rand *rand.Rand, size int) reflect.Value { + m := &certificateRequestMsg13{} + m.requestContext = randomBytes(rand.Intn(5), rand) + m.supportedSignatureAlgorithms = supportedSignatureAlgorithms + numCAs := rand.Intn(100) + m.certificateAuthorities = make([][]byte, numCAs) + for i := 0; i < numCAs; i++ { + m.certificateAuthorities[i] = randomBytes(rand.Intn(15)+1, rand) + } + return reflect.ValueOf(m) +} + func (*certificateVerifyMsg) Generate(rand *rand.Rand, size int) reflect.Value { m := &certificateVerifyMsg{} m.signature = randomBytes(rand.Intn(15)+1, rand)