diff options
author | David Benjamin <davidben@google.com> | 2017-11-24 10:00:39 -0500 |
---|---|---|
committer | CQ bot account: commit-bot@chromium.org <commit-bot@chromium.org> | 2017-11-27 21:18:40 +0000 |
commit | be8c8b4b1dd26f5d66d603987f28fc3084fbf217 (patch) | |
tree | 548480c6b6875b0f72b4d72ed39bf3e0d21f0bb7 | |
parent | 8c9ceadc58f425bd5edc71c349ef55e1a516302d (diff) | |
download | boringssl-be8c8b4b1dd26f5d66d603987f28fc3084fbf217.zip boringssl-be8c8b4b1dd26f5d66d603987f28fc3084fbf217.tar.gz boringssl-be8c8b4b1dd26f5d66d603987f28fc3084fbf217.tar.bz2 |
runner: Add a byteReader type and convert ClientHello parsing.
Bug: 212
Change-Id: Iecbd8fddef1b55a438947ad60780e08cb4260c48
Reviewed-on: https://boringssl-review.googlesource.com/23444
Reviewed-by: Steven Valdez <svaldez@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
CQ-Verified: CQ bot account: commit-bot@chromium.org <commit-bot@chromium.org>
-rw-r--r-- | ssl/test/runner/handshake_messages.go | 444 |
1 files changed, 228 insertions, 216 deletions
diff --git a/ssl/test/runner/handshake_messages.go b/ssl/test/runner/handshake_messages.go index 93da121..766d515 100644 --- a/ssl/test/runner/handshake_messages.go +++ b/ssl/test/runner/handshake_messages.go @@ -125,6 +125,121 @@ func (bb *byteBuilder) discardChild() { bb.child = nil } +type byteReader []byte + +func (br *byteReader) readInternal(out *byteReader, n int) bool { + if len(*br) < n { + return false + } + *out = (*br)[:n] + *br = (*br)[n:] + return true +} + +func (br *byteReader) readBytes(out *[]byte, n int) bool { + var child byteReader + if !br.readInternal(&child, n) { + return false + } + *out = []byte(child) + return true +} + +func (br *byteReader) readUint(out *uint64, n int) bool { + var b []byte + if !br.readBytes(&b, n) { + return false + } + *out = 0 + for _, v := range b { + *out <<= 8 + *out |= uint64(v) + } + return true +} + +func (br *byteReader) readU8(out *uint8) bool { + var b []byte + if !br.readBytes(&b, 1) { + return false + } + *out = b[0] + return true +} + +func (br *byteReader) readU16(out *uint16) bool { + var v uint64 + if !br.readUint(&v, 2) { + return false + } + *out = uint16(v) + return true +} + +func (br *byteReader) readU24(out *uint32) bool { + var v uint64 + if !br.readUint(&v, 3) { + return false + } + *out = uint32(v) + return true +} + +func (br *byteReader) readU32(out *uint32) bool { + var v uint64 + if !br.readUint(&v, 4) { + return false + } + *out = uint32(v) + return true +} + +func (br *byteReader) readU64(out *uint64) bool { + return br.readUint(out, 8) +} + +func (br *byteReader) readLengthPrefixed(out *byteReader, n int) bool { + var length uint64 + return br.readUint(&length, n) && + uint64(len(*br)) >= length && + br.readInternal(out, int(length)) +} + +func (br *byteReader) readLengthPrefixedBytes(out *[]byte, n int) bool { + var length uint64 + return br.readUint(&length, n) && + uint64(len(*br)) >= length && + br.readBytes(out, int(length)) +} + +func (br *byteReader) readU8LengthPrefixed(out *byteReader) bool { + return br.readLengthPrefixed(out, 1) +} +func (br *byteReader) readU8LengthPrefixedBytes(out *[]byte) bool { + return br.readLengthPrefixedBytes(out, 1) +} + +func (br *byteReader) readU16LengthPrefixed(out *byteReader) bool { + return br.readLengthPrefixed(out, 2) +} +func (br *byteReader) readU16LengthPrefixedBytes(out *[]byte) bool { + return br.readLengthPrefixedBytes(out, 2) +} + +func (br *byteReader) readU24LengthPrefixed(out *byteReader) bool { + return br.readLengthPrefixed(out, 3) +} +func (br *byteReader) readU24LengthPrefixedBytes(out *[]byte) bool { + return br.readLengthPrefixedBytes(out, 3) +} + +func (br *byteReader) readU32LengthPrefixed(out *byteReader) bool { + return br.readLengthPrefixed(out, 4) +} +func (br *byteReader) readU32LengthPrefixedBytes(out *[]byte) bool { + return br.readLengthPrefixedBytes(out, 4) +} + type keyShareEntry struct { group CurveID keyExchange []byte @@ -483,57 +598,37 @@ func (m *clientHelloMsg) marshal() []byte { } func (m *clientHelloMsg) unmarshal(data []byte) bool { - if len(data) < 42 { - return false - } m.raw = data - m.vers = uint16(data[4])<<8 | uint16(data[5]) - m.random = data[6:38] - sessionIdLen := int(data[38]) - if sessionIdLen > 32 || len(data) < 39+sessionIdLen { + reader := byteReader(data[4:]) + if !reader.readU16(&m.vers) || + !reader.readBytes(&m.random, 32) || + !reader.readU8LengthPrefixedBytes(&m.sessionId) || + len(m.sessionId) > 32 { return false } - m.sessionId = data[39 : 39+sessionIdLen] - data = data[39+sessionIdLen:] if m.isDTLS { - if len(data) < 1 { + if !reader.readU8LengthPrefixedBytes(&m.cookie) || + len(m.cookie) > 32 { return false } - cookieLen := int(data[0]) - if cookieLen > 32 || len(data) < 1+cookieLen { - return false - } - m.cookie = data[1 : 1+cookieLen] - data = data[1+cookieLen:] } - if len(data) < 2 { + var cipherSuites byteReader + if !reader.readU16LengthPrefixed(&cipherSuites) || + !reader.readU8LengthPrefixedBytes(&m.compressionMethods) { return false } - // cipherSuiteLen is the number of bytes of cipher suite numbers. Since - // they are uint16s, the number must be even. - cipherSuiteLen := int(data[0])<<8 | int(data[1]) - if cipherSuiteLen%2 == 1 || len(data) < 2+cipherSuiteLen { - return false - } - numCipherSuites := cipherSuiteLen / 2 - m.cipherSuites = make([]uint16, numCipherSuites) - for i := 0; i < numCipherSuites; i++ { - m.cipherSuites[i] = uint16(data[2+2*i])<<8 | uint16(data[3+2*i]) - if m.cipherSuites[i] == scsvRenegotiation { + + m.cipherSuites = make([]uint16, 0, len(cipherSuites)/2) + for len(cipherSuites) > 0 { + var v uint16 + if !cipherSuites.readU16(&v) { + return false + } + m.cipherSuites = append(m.cipherSuites, v) + if v == scsvRenegotiation { m.secureRenegotiation = []byte{} } } - data = data[2+cipherSuiteLen:] - if len(data) < 1 { - return false - } - compressionMethodsLen := int(data[0]) - if len(data) < 1+compressionMethodsLen { - return false - } - m.compressionMethods = data[1 : 1+compressionMethodsLen] - - data = data[1+compressionMethodsLen:] m.nextProtoNeg = false m.serverName = "" @@ -549,167 +644,108 @@ func (m *clientHelloMsg) unmarshal(data []byte) bool { m.extendedMasterSecret = false m.customExtension = "" - if len(data) == 0 { + if len(reader) == 0 { // ClientHello is optionally followed by extension data return true } - if len(data) < 2 { - return false - } - extensionsLength := int(data[0])<<8 | int(data[1]) - data = data[2:] - if extensionsLength != len(data) { + var extensions byteReader + if !reader.readU16LengthPrefixed(&extensions) || len(reader) != 0 { return false } - - for len(data) != 0 { - if len(data) < 4 { + for len(extensions) > 0 { + var extension uint16 + var body byteReader + if !extensions.readU16(&extension) || + !extensions.readU16LengthPrefixed(&body) { return false } - extension := uint16(data[0])<<8 | uint16(data[1]) - length := int(data[2])<<8 | int(data[3]) - data = data[4:] - if len(data) < length { - return false - } - switch extension { case extensionServerName: - if length < 2 { + var names byteReader + if !body.readU16LengthPrefixed(&names) || len(body) != 0 { return false } - numNames := int(data[0])<<8 | int(data[1]) - d := data[2:] - for i := 0; i < numNames; i++ { - if len(d) < 3 { - return false - } - nameType := d[0] - nameLen := int(d[1])<<8 | int(d[2]) - d = d[3:] - if len(d) < nameLen { + for len(names) > 0 { + var nameType byte + var name []byte + if !names.readU8(&nameType) || + !names.readU16LengthPrefixedBytes(&name) { return false } if nameType == 0 { - m.serverName = string(d[0:nameLen]) - break + m.serverName = string(name) } - d = d[nameLen:] } case extensionNextProtoNeg: - if length > 0 { + if len(body) != 0 { return false } m.nextProtoNeg = true case extensionStatusRequest: - m.ocspStapling = length > 0 && data[0] == statusTypeOCSP + m.ocspStapling = len(body) > 0 && body[0] == statusTypeOCSP case extensionSupportedCurves: // http://tools.ietf.org/html/rfc4492#section-5.5.1 - if length < 2 { + var curves byteReader + if !body.readU16LengthPrefixed(&curves) || len(body) != 0 { return false } - l := int(data[0])<<8 | int(data[1]) - if l%2 == 1 || length != l+2 { - return false - } - numCurves := l / 2 - m.supportedCurves = make([]CurveID, numCurves) - d := data[2:] - for i := 0; i < numCurves; i++ { - m.supportedCurves[i] = CurveID(d[0])<<8 | CurveID(d[1]) - d = d[2:] + m.supportedCurves = make([]CurveID, 0, len(curves)/2) + for len(curves) > 0 { + var v uint16 + if !curves.readU16(&v) { + return false + } + m.supportedCurves = append(m.supportedCurves, CurveID(v)) } case extensionSupportedPoints: // http://tools.ietf.org/html/rfc4492#section-5.5.2 - if length < 1 { + if !body.readU8LengthPrefixedBytes(&m.supportedPoints) || len(body) != 0 { return false } - l := int(data[0]) - if length != l+1 { - return false - } - m.supportedPoints = data[1 : 1+l] case extensionSessionTicket: // http://tools.ietf.org/html/rfc5077#section-3.2 m.ticketSupported = true - m.sessionTicket = data[:length] + m.sessionTicket = []byte(body) case extensionKeyShare: // draft-ietf-tls-tls13 section 6.3.2.3 - if length < 2 { - return false - } - l := int(data[0])<<8 | int(data[1]) - if l != length-2 { + var keyShares byteReader + if !body.readU16LengthPrefixed(&keyShares) || len(body) != 0 { return false } - d := data[2:length] m.hasKeyShares = true - for len(d) > 0 { - // The next KeyShareEntry contains a NamedGroup (2 bytes) and a - // key_exchange (2-byte length prefix with at least 1 byte of content). - if len(d) < 5 { + for len(keyShares) > 0 { + var entry keyShareEntry + var group uint16 + if !keyShares.readU16(&group) || + !keyShares.readU16LengthPrefixedBytes(&entry.keyExchange) { return false } - entry := keyShareEntry{} - entry.group = CurveID(d[0])<<8 | CurveID(d[1]) - keyExchLen := int(d[2])<<8 | int(d[3]) - d = d[4:] - if len(d) < keyExchLen { - return false - } - entry.keyExchange = d[:keyExchLen] - d = d[keyExchLen:] + entry.group = CurveID(group) m.keyShares = append(m.keyShares, entry) } case extensionPreSharedKey: // draft-ietf-tls-tls13-18 section 4.2.6 - if length < 2 { + var psks, binders byteReader + if !body.readU16LengthPrefixed(&psks) || + !body.readU16LengthPrefixed(&binders) || + len(body) != 0 { return false } - l := int(data[0])<<8 | int(data[1]) - d := data[2 : l+2] - // Parse PSK identities. - for len(d) > 0 { - if len(d) < 2 { - return false - } - pskLen := int(d[0])<<8 | int(d[1]) - d = d[2:] - - if len(d) < pskLen+4 { + for len(psks) > 0 { + var psk pskIdentity + if !psks.readU16LengthPrefixedBytes(&psk.ticket) || + !psks.readU32(&psk.obfuscatedTicketAge) { return false } - ticket := d[:pskLen] - obfuscatedTicketAge := uint32(d[pskLen])<<24 | uint32(d[pskLen+1])<<16 | uint32(d[pskLen+2])<<8 | uint32(d[pskLen+3]) - psk := pskIdentity{ - ticket: ticket, - obfuscatedTicketAge: obfuscatedTicketAge, - } m.pskIdentities = append(m.pskIdentities, psk) - d = d[pskLen+4:] - } - d = data[l+2:] - if len(d) < 2 { - return false - } - l = int(d[0])<<8 | int(d[1]) - d = d[2:] - if l != len(d) { - return false } - // Parse PSK binders. - for len(d) > 0 { - if len(d) < 1 { - return false - } - binderLen := int(d[0]) - d = d[1:] - if binderLen > len(d) { + for len(binders) > 0 { + var binder []byte + if !binders.readU8LengthPrefixedBytes(&binder) { return false } - m.pskBinders = append(m.pskBinders, d[:binderLen]) - d = d[binderLen:] + m.pskBinders = append(m.pskBinders, binder) } // There must be the same number of identities as binders. @@ -718,121 +754,97 @@ func (m *clientHelloMsg) unmarshal(data []byte) bool { } case extensionPSKKeyExchangeModes: // draft-ietf-tls-tls13-18 section 4.2.7 - if length < 1 { + if !body.readU8LengthPrefixedBytes(&m.pskKEModes) || len(body) != 0 { return false } - l := int(data[0]) - if l != length-1 { - return false - } - m.pskKEModes = data[1:length] case extensionEarlyData: // draft-ietf-tls-tls13 section 6.3.2.5 - if length != 0 { + if len(body) != 0 { return false } m.hasEarlyData = true case extensionCookie: - if length < 2 { + if !body.readU16LengthPrefixedBytes(&m.tls13Cookie) || len(body) != 0 { return false } - l := int(data[0])<<8 | int(data[1]) - if l != length-2 || l == 0 { - return false - } - m.tls13Cookie = data[2 : 2+l] case extensionSignatureAlgorithms: // https://tools.ietf.org/html/rfc5246#section-7.4.1.4.1 - if length < 2 || length&1 != 0 { + var sigAlgs byteReader + if !body.readU16LengthPrefixed(&sigAlgs) || len(body) != 0 { return false } - l := int(data[0])<<8 | int(data[1]) - if l != length-2 { - return false - } - n := l / 2 - d := data[2:] - m.signatureAlgorithms = make([]signatureAlgorithm, n) - for i := range m.signatureAlgorithms { - m.signatureAlgorithms[i] = signatureAlgorithm(d[0])<<8 | signatureAlgorithm(d[1]) - d = d[2:] + m.signatureAlgorithms = make([]signatureAlgorithm, 0, len(sigAlgs)/2) + for len(sigAlgs) > 0 { + var v uint16 + if !sigAlgs.readU16(&v) { + return false + } + m.signatureAlgorithms = append(m.signatureAlgorithms, signatureAlgorithm(v)) } case extensionSupportedVersions: - if length < 1+2 { + var versions byteReader + if !body.readU8LengthPrefixed(&versions) || len(body) != 0 { return false } - l := int(data[0]) - if l != length-1 || l%2 == 1 || l < 2 { - return false - } - n := l / 2 - d := data[1:] - m.supportedVersions = make([]uint16, n) - for i := range m.supportedVersions { - m.supportedVersions[i] = uint16(d[0])<<8 | uint16(d[1]) - d = d[2:] + m.supportedVersions = make([]uint16, 0, len(versions)/2) + for len(versions) > 0 { + var v uint16 + if !versions.readU16(&v) { + return false + } + m.supportedVersions = append(m.supportedVersions, v) } case extensionRenegotiationInfo: - if length < 1 || length != int(data[0])+1 { + if !body.readU8LengthPrefixedBytes(&m.secureRenegotiation) || len(body) != 0 { return false } - m.secureRenegotiation = data[1:length] case extensionALPN: - if length < 2 { - return false - } - l := int(data[0])<<8 | int(data[1]) - if l != length-2 { + var protocols byteReader + if !body.readU16LengthPrefixed(&protocols) || len(body) != 0 { return false } - d := data[2:length] - for len(d) != 0 { - stringLen := int(d[0]) - d = d[1:] - if stringLen == 0 || stringLen > len(d) { + for len(protocols) > 0 { + var protocol []byte + if !protocols.readU8LengthPrefixedBytes(&protocol) { return false } - m.alpnProtocols = append(m.alpnProtocols, string(d[:stringLen])) - d = d[stringLen:] + m.alpnProtocols = append(m.alpnProtocols, string(protocol)) } case extensionChannelID: - if length > 0 { + if len(body) != 0 { return false } m.channelIDSupported = true case extensionExtendedMasterSecret: - if length != 0 { + if len(body) != 0 { return false } m.extendedMasterSecret = true case extensionUseSRTP: - if length < 2 { + var profiles byteReader + var mki []byte + if !body.readU16LengthPrefixed(&profiles) || + !body.readU8LengthPrefixedBytes(&mki) || + len(body) != 0 { return false } - l := int(data[0])<<8 | int(data[1]) - if l > length-2 || l%2 != 0 { - return false - } - n := l / 2 - m.srtpProtectionProfiles = make([]uint16, n) - d := data[2:length] - for i := 0; i < n; i++ { - m.srtpProtectionProfiles[i] = uint16(d[0])<<8 | uint16(d[1]) - d = d[2:] - } - if len(d) < 1 || int(d[0]) != len(d)-1 { - return false + m.srtpProtectionProfiles = make([]uint16, 0, len(profiles)/2) + for len(profiles) > 0 { + var v uint16 + if !profiles.readU16(&v) { + return false + } + m.srtpProtectionProfiles = append(m.srtpProtectionProfiles, v) } - m.srtpMasterKeyIdentifier = string(d[1:]) + m.srtpMasterKeyIdentifier = string(mki) case extensionSignedCertificateTimestamp: - if length != 0 { + if len(body) != 0 { return false } m.sctListSupported = true case extensionCustom: - m.customExtension = string(data[:length]) + m.customExtension = string(body) } - data = data[length:] if isGREASEValue(extension) { m.hasGREASEExtension = true |