From d660b57208e433d7d64973d52c3ea06a77ec4b57 Mon Sep 17 00:00:00 2001 From: David Benjamin Date: Sat, 31 Jan 2015 15:13:21 -0500 Subject: [PATCH] runner: Refactor handshake fragmenting slightly. No behavior change. This is in preparation for buffering a flight of handshake messages to reorder vigorously on flush. Change-Id: Ic348829b340bf58d28f332027646559cb11046ac Reviewed-on: https://boringssl-review.googlesource.com/3215 Reviewed-by: Adam Langley --- ssl/test/runner/dtls.go | 191 ++++++++++++++++++++-------------------- 1 file changed, 97 insertions(+), 94 deletions(-) diff --git a/ssl/test/runner/dtls.go b/ssl/test/runner/dtls.go index 2e1fb65a..8e4b8a6e 100644 --- a/ssl/test/runner/dtls.go +++ b/ssl/test/runner/dtls.go @@ -106,125 +106,128 @@ func (c *Conn) dtlsDoReadRecord(want recordType) (recordType, *block, error) { } func (c *Conn) dtlsWriteRecord(typ recordType, data []byte) (n int, err error) { - recordHeaderLen := dtlsRecordHeaderLen + if typ != recordTypeHandshake { + // Only handshake messages are fragmented. + return c.dtlsWriteRawRecord(typ, data) + } + maxLen := c.config.Bugs.MaxHandshakeRecordLength if maxLen <= 0 { maxLen = 1024 } - b := c.out.newBlock() - - var header []byte - if typ == recordTypeHandshake { - // Handshake messages have to be modified to include - // fragment offset and length and with the header - // replicated. Save the header here. - // - // TODO(davidben): This assumes that data contains - // exactly one handshake message. This is incompatible - // with FragmentAcrossChangeCipherSpec. (Which is - // unfortunate because OpenSSL's DTLS implementation - // will probably accept such fragmentation and could - // do with a fix + tests.) - if len(data) < 4 { - // This should not happen. - panic(data) - } - header = data[:4] - data = data[4:] + // Handshake messages have to be modified to include fragment + // offset and length and with the header replicated. Save the + // TLS header here. + // + // TODO(davidben): This assumes that data contains exactly one + // handshake message. This is incompatible with + // FragmentAcrossChangeCipherSpec. (Which is unfortunate + // because OpenSSL's DTLS implementation will probably accept + // such fragmentation and could do with a fix + tests.) + if len(data) < 4 { + // This should not happen. + panic(data) } + header := data[:4] + data = data[4:] firstRun := true for firstRun || len(data) > 0 { firstRun = false m := len(data) - var fragment []byte - // Handshake messages get fragmented. Other records we - // pass-through as is. DTLS should be a packet - // interface. - if typ == recordTypeHandshake { - if m > maxLen { - m = maxLen - } - - // Standard handshake header. - fragment = make([]byte, 0, 12+m) - fragment = append(fragment, header...) - // message_seq - fragment = append(fragment, byte(c.sendHandshakeSeq>>8), byte(c.sendHandshakeSeq)) - // fragment_offset - fragment = append(fragment, byte(n>>16), byte(n>>8), byte(n)) - // fragment_length - fragment = append(fragment, byte(m>>16), byte(m>>8), byte(m)) - fragment = append(fragment, data[:m]...) - } else { - fragment = data[:m] + if m > maxLen { + m = maxLen } - // Send the fragment. - explicitIVLen := 0 - explicitIVIsSeq := false - - if cbc, ok := c.out.cipher.(cbcMode); ok { - // Block cipher modes have an explicit IV. - explicitIVLen = cbc.BlockSize() - } else if _, ok := c.out.cipher.(cipher.AEAD); ok { - explicitIVLen = 8 - // The AES-GCM construction in TLS has an - // explicit nonce so that the nonce can be - // random. However, the nonce is only 8 bytes - // which is too small for a secure, random - // nonce. Therefore we use the sequence number - // as the nonce. - explicitIVIsSeq = true - } else if c.out.cipher != nil { - panic("Unknown cipher") - } - b.resize(recordHeaderLen + explicitIVLen + len(fragment)) - b.data[0] = byte(typ) - vers := c.vers - if vers == 0 { - // Some TLS servers fail if the record version is - // greater than TLS 1.0 for the initial ClientHello. - vers = VersionTLS10 - } - vers = versionToWire(vers, c.isDTLS) - b.data[1] = byte(vers >> 8) - b.data[2] = byte(vers) - // DTLS records include an explicit sequence number. - copy(b.data[3:11], c.out.seq[0:]) - b.data[11] = byte(len(fragment) >> 8) - b.data[12] = byte(len(fragment)) - if explicitIVLen > 0 { - explicitIV := b.data[recordHeaderLen : recordHeaderLen+explicitIVLen] - if explicitIVIsSeq { - copy(explicitIV, c.out.seq[:]) - } else { - if _, err = io.ReadFull(c.config.rand(), explicitIV); err != nil { - break - } - } - } - copy(b.data[recordHeaderLen+explicitIVLen:], fragment) - c.out.encrypt(b, explicitIVLen) + // Standard TLS handshake header. + fragment := make([]byte, 0, 12+m) + fragment = append(fragment, header...) + // message_seq + fragment = append(fragment, byte(c.sendHandshakeSeq>>8), byte(c.sendHandshakeSeq)) + // fragment_offset + fragment = append(fragment, byte(n>>16), byte(n>>8), byte(n)) + // fragment_length + fragment = append(fragment, byte(m>>16), byte(m>>8), byte(m)) + fragment = append(fragment, data[:m]...) // TODO(davidben): A real DTLS implementation needs to - // retransmit handshake messages. For testing - // purposes, we don't actually care. - _, err = c.conn.Write(b.data) + // retransmit handshake messages. For testing purposes, we don't + // actually care. + _, err = c.dtlsWriteRawRecord(recordTypeHandshake, fragment) if err != nil { break } n += m data = data[m:] } - c.out.freeBlock(b) // Increment the handshake sequence number for the next // handshake message. - if typ == recordTypeHandshake { - c.sendHandshakeSeq++ + c.sendHandshakeSeq++ + return +} + +func (c *Conn) dtlsWriteRawRecord(typ recordType, data []byte) (n int, err error) { + recordHeaderLen := dtlsRecordHeaderLen + maxLen := c.config.Bugs.MaxHandshakeRecordLength + if maxLen <= 0 { + maxLen = 1024 + } + + b := c.out.newBlock() + + explicitIVLen := 0 + explicitIVIsSeq := false + + if cbc, ok := c.out.cipher.(cbcMode); ok { + // Block cipher modes have an explicit IV. + explicitIVLen = cbc.BlockSize() + } else if _, ok := c.out.cipher.(cipher.AEAD); ok { + explicitIVLen = 8 + // The AES-GCM construction in TLS has an explicit nonce so that + // the nonce can be random. However, the nonce is only 8 bytes + // which is too small for a secure, random nonce. Therefore we + // use the sequence number as the nonce. + explicitIVIsSeq = true + } else if c.out.cipher != nil { + panic("Unknown cipher") } + b.resize(recordHeaderLen + explicitIVLen + len(data)) + b.data[0] = byte(typ) + vers := c.vers + if vers == 0 { + // Some TLS servers fail if the record version is greater than + // TLS 1.0 for the initial ClientHello. + vers = VersionTLS10 + } + vers = versionToWire(vers, c.isDTLS) + b.data[1] = byte(vers >> 8) + b.data[2] = byte(vers) + // DTLS records include an explicit sequence number. + copy(b.data[3:11], c.out.seq[0:]) + b.data[11] = byte(len(data) >> 8) + b.data[12] = byte(len(data)) + if explicitIVLen > 0 { + explicitIV := b.data[recordHeaderLen : recordHeaderLen+explicitIVLen] + if explicitIVIsSeq { + copy(explicitIV, c.out.seq[:]) + } else { + if _, err = io.ReadFull(c.config.rand(), explicitIV); err != nil { + return + } + } + } + copy(b.data[recordHeaderLen+explicitIVLen:], data) + c.out.encrypt(b, explicitIVLen) + + _, err = c.conn.Write(b.data) + if err != nil { + return + } + n = len(data) + + c.out.freeBlock(b) if typ == recordTypeChangeCipherSpec { err = c.out.changeCipherSpec(c.config)