|
|
@@ -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) |
|
|
|