Split off DTLS record header parsing in test runner. The DTLS 1.3 record header is formatted differently than the old record header, but the code to read/process a DTLS record mixes record header parsing with other record processing code. This change provides a clear delineation between processing the record header and processing the record, which will assist in adding support for the DTLS 1.3 record header. Bug: 715 Change-Id: I13a0bb5c184e79b88f064e9ac8ecbc82eb56750a Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/69950 Commit-Queue: Bob Beck <bbe@google.com> Reviewed-by: Bob Beck <bbe@google.com>
diff --git a/ssl/test/runner/dtls.go b/ssl/test/runner/dtls.go index 95e1a9a..1893afe 100644 --- a/ssl/test/runner/dtls.go +++ b/ssl/test/runner/dtls.go
@@ -23,29 +23,13 @@ "net" ) -func (c *Conn) dtlsDoReadRecord(want recordType) (recordType, *block, error) { - recordHeaderLen := dtlsMaxRecordHeaderLen - - if c.rawInput == nil { - c.rawInput = c.in.newBlock() - } - b := c.rawInput - - // Read a new packet only if the current one is empty. - var newPacket bool - if len(b.data) == 0 { - // Pick some absurdly large buffer size. - b.resize(maxCiphertext + recordHeaderLen) - n, err := c.conn.Read(c.rawInput.data) - if err != nil { - return 0, nil, err - } - if c.config.Bugs.MaxPacketLength != 0 && n > c.config.Bugs.MaxPacketLength { - return 0, nil, fmt.Errorf("dtls: exceeded maximum packet length") - } - c.rawInput.resize(n) - newPacket = true - } +// readDTLSRecordHeader reads the record header from the block. Based on the +// header it reads, it checks the header's validity and sets appropriate state +// as needed. This function returns the record header, the record type indicated +// in the header (if it contains the type), and the sequence number to use for +// record decryption. +func (c *Conn) readDTLSRecordHeader(b *block) (headerLen int, recordLen int, typ recordType, seq []byte, err error) { + recordHeaderLen := 13 // Read out one record. // @@ -53,9 +37,9 @@ // but this is test code. We should not be tolerant of our // peer sending garbage. if len(b.data) < recordHeaderLen { - return 0, nil, errors.New("dtls: failed to read record header") + return 0, 0, 0, nil, errors.New("dtls: failed to read record header") } - typ := recordType(b.data[0]) + typ = recordType(b.data[0]) vers := uint16(b.data[1])<<8 | uint16(b.data[2]) // Alerts sent near version negotiation do not have a well-defined // record-layer version prior to TLS 1.3. (In TLS 1.3, the record-layer @@ -68,32 +52,61 @@ } if vers != wireVersion { c.sendAlert(alertProtocolVersion) - return 0, nil, c.in.setErrorLocked(fmt.Errorf("dtls: received record with version %x when expecting version %x", vers, c.wireVersion)) + return 0, 0, 0, nil, c.in.setErrorLocked(fmt.Errorf("dtls: received record with version %x when expecting version %x", vers, c.wireVersion)) } } else { // Pre-version-negotiation alerts may be sent with any version. if expect := c.config.Bugs.ExpectInitialRecordVersion; expect != 0 && vers != expect { c.sendAlert(alertProtocolVersion) - return 0, nil, c.in.setErrorLocked(fmt.Errorf("dtls: received record with version %x when expecting version %x", vers, expect)) + return 0, 0, 0, nil, c.in.setErrorLocked(fmt.Errorf("dtls: received record with version %x when expecting version %x", vers, expect)) } } } epoch := b.data[3:5] - seq := b.data[5:11] + seq = b.data[5:11] // For test purposes, require the sequence number be monotonically // increasing, so c.in includes the minimum next sequence number. Gaps // may occur if packets failed to be sent out. A real implementation // would maintain a replay window and such. if !bytes.Equal(epoch, c.in.seq[:2]) { c.sendAlert(alertIllegalParameter) - return 0, nil, c.in.setErrorLocked(fmt.Errorf("dtls: bad epoch, want %x, got %x", c.in.seq[:2], epoch)) + return 0, 0, 0, nil, c.in.setErrorLocked(fmt.Errorf("dtls: bad epoch")) } if bytes.Compare(seq, c.in.seq[2:]) < 0 { c.sendAlert(alertIllegalParameter) - return 0, nil, c.in.setErrorLocked(fmt.Errorf("dtls: bad sequence number")) + return 0, 0, 0, nil, c.in.setErrorLocked(fmt.Errorf("dtls: bad sequence number")) } copy(c.in.seq[2:], seq) - n := int(b.data[11])<<8 | int(b.data[12]) + recordLen = int(b.data[11])<<8 | int(b.data[12]) + return recordHeaderLen, recordLen, typ, b.data[3:11], nil +} + +func (c *Conn) dtlsDoReadRecord(want recordType) (recordType, *block, error) { + if c.rawInput == nil { + c.rawInput = c.in.newBlock() + } + b := c.rawInput + + // Read a new packet only if the current one is empty. + var newPacket bool + if len(b.data) == 0 { + // Pick some absurdly large buffer size. + b.resize(maxCiphertext + dtlsMaxRecordHeaderLen) + n, err := c.conn.Read(c.rawInput.data) + if err != nil { + return 0, nil, err + } + if c.config.Bugs.MaxPacketLength != 0 && n > c.config.Bugs.MaxPacketLength { + return 0, nil, fmt.Errorf("dtls: exceeded maximum packet length") + } + c.rawInput.resize(n) + newPacket = true + } + + recordHeaderLen, n, typ, seq, err := c.readDTLSRecordHeader(b) + if err != nil { + return 0, nil, err + } if n > maxCiphertext || len(b.data) < recordHeaderLen+n { c.sendAlert(alertRecordOverflow) return 0, nil, c.in.setErrorLocked(fmt.Errorf("dtls: oversized record received with length %d", n)) @@ -101,7 +114,7 @@ b, c.rawInput = c.in.splitBlock(b, recordHeaderLen+n) // Process message. - ok, off, _, alertValue := c.in.decrypt(b.data[3:11], recordHeaderLen, b) + ok, off, _, alertValue := c.in.decrypt(seq, recordHeaderLen, b) if !ok { // A real DTLS implementation would silently ignore bad records, // but we want to notice errors from the implementation under