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 <agl@google.com>
This commit is contained in:
David Benjamin 2015-01-31 15:13:21 -05:00 committed by Adam Langley
parent 79ae85e4f7
commit d660b57208

View File

@ -106,6 +106,69 @@ func (c *Conn) dtlsDoReadRecord(want recordType) (recordType, *block, error) {
}
func (c *Conn) dtlsWriteRecord(typ recordType, data []byte) (n int, err error) {
if typ != recordTypeHandshake {
// Only handshake messages are fragmented.
return c.dtlsWriteRawRecord(typ, data)
}
maxLen := c.config.Bugs.MaxHandshakeRecordLength
if maxLen <= 0 {
maxLen = 1024
}
// 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)
if m > maxLen {
m = maxLen
}
// 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.dtlsWriteRawRecord(recordTypeHandshake, fragment)
if err != nil {
break
}
n += m
data = data[m:]
}
// Increment the handshake sequence number for the next
// handshake message.
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 {
@ -114,118 +177,58 @@ func (c *Conn) dtlsWriteRecord(typ recordType, data []byte) (n int, err error) {
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:]
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")
}
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]...)
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 {
fragment = data[:m]
}
// 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
}
if _, err = io.ReadFull(c.config.rand(), explicitIV); err != nil {
return
}
}
copy(b.data[recordHeaderLen+explicitIVLen:], fragment)
c.out.encrypt(b, explicitIVLen)
// 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)
if err != nil {
break
}
n += m
data = data[m:]
}
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)
// Increment the handshake sequence number for the next
// handshake message.
if typ == recordTypeHandshake {
c.sendHandshakeSeq++
}
if typ == recordTypeChangeCipherSpec {
err = c.out.changeCipherSpec(c.config)
if err != nil {