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:
parent
79ae85e4f7
commit
d660b57208
@ -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 {
|
||||
|
Loading…
Reference in New Issue
Block a user