Test-only DTLS implementation in runner.go.

Run against openssl s_client and openssl s_server. This seems to work for a
start, although it may need to become cleverer to stress more of BoringSSL's
implementation for test purposes.

In particular, it assumes a reliable, in-order channel. And it requires that
the peer send handshake fragments in order. Retransmit and whatnot are not
implemented. The peer under test will be expected to handle a lossy channel,
but all loss in the channel will be controlled. MAC errors, etc., are fatal.

Change-Id: I329233cfb0994938fd012667ddf7c6a791ac7164
Reviewed-on: https://boringssl-review.googlesource.com/1390
Reviewed-by: Adam Langley <agl@google.com>
This commit is contained in:
David Benjamin
2014-08-13 23:43:38 +00:00
committed by Adam Langley
parent 51e3283d62
commit 83c0bc94d7
8 changed files with 761 additions and 145 deletions
+12 -8
View File
@@ -52,6 +52,9 @@ const (
// suiteSHA384 indicates that the cipher suite uses SHA384 as the
// handshake hash.
suiteSHA384
// suiteNoDTLS indicates that the cipher suite cannot be used
// in DTLS.
suiteNoDTLS
)
// A cipherSuite is a specific combination of key agreement, cipher and MAC
@@ -76,8 +79,8 @@ var cipherSuites = []*cipherSuite{
{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, 16, 0, 4, ecdheRSAKA, suiteECDHE | suiteTLS12, nil, nil, aeadAESGCM},
{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, 16, 0, 4, ecdheECDSAKA, suiteECDHE | suiteECDSA | suiteTLS12, nil, nil, aeadAESGCM},
{TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, 32, 0, 4, ecdheRSAKA, suiteECDHE | suiteTLS12 | suiteSHA384, nil, nil, aeadAESGCM},
{TLS_ECDHE_RSA_WITH_RC4_128_SHA, 16, 20, 0, ecdheRSAKA, suiteECDHE, cipherRC4, macSHA1, nil},
{TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, 16, 20, 0, ecdheECDSAKA, suiteECDHE | suiteECDSA, cipherRC4, macSHA1, nil},
{TLS_ECDHE_RSA_WITH_RC4_128_SHA, 16, 20, 0, ecdheRSAKA, suiteECDHE | suiteNoDTLS, cipherRC4, macSHA1, nil},
{TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, 16, 20, 0, ecdheECDSAKA, suiteECDHE | suiteECDSA | suiteNoDTLS, cipherRC4, macSHA1, nil},
{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, 16, 20, 16, ecdheRSAKA, suiteECDHE, cipherAES, macSHA1, nil},
{TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, 16, 20, 16, ecdheECDSAKA, suiteECDHE | suiteECDSA, cipherAES, macSHA1, nil},
{TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, 32, 20, 16, ecdheRSAKA, suiteECDHE, cipherAES, macSHA1, nil},
@@ -88,8 +91,8 @@ var cipherSuites = []*cipherSuite{
{TLS_DHE_RSA_WITH_AES_256_CBC_SHA, 32, 20, 16, dheRSAKA, 0, cipherAES, macSHA1, nil},
{TLS_RSA_WITH_AES_128_GCM_SHA256, 16, 0, 4, rsaKA, suiteTLS12, nil, nil, aeadAESGCM},
{TLS_RSA_WITH_AES_256_GCM_SHA384, 32, 0, 4, rsaKA, suiteTLS12 | suiteSHA384, nil, nil, aeadAESGCM},
{TLS_RSA_WITH_RC4_128_SHA, 16, 20, 0, rsaKA, 0, cipherRC4, macSHA1, nil},
{TLS_RSA_WITH_RC4_128_MD5, 16, 16, 0, rsaKA, 0, cipherRC4, macMD5, nil},
{TLS_RSA_WITH_RC4_128_SHA, 16, 20, 0, rsaKA, suiteNoDTLS, cipherRC4, macSHA1, nil},
{TLS_RSA_WITH_RC4_128_MD5, 16, 16, 0, rsaKA, suiteNoDTLS, cipherRC4, macMD5, nil},
{TLS_RSA_WITH_AES_128_CBC_SHA, 16, 20, 16, rsaKA, 0, cipherAES, macSHA1, nil},
{TLS_RSA_WITH_AES_256_CBC_SHA, 32, 20, 16, rsaKA, 0, cipherAES, macSHA1, nil},
{TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, 24, 20, 8, ecdheRSAKA, suiteECDHE, cipher3DES, macSHA1, nil},
@@ -145,7 +148,7 @@ func macMD5(version uint16, key []byte) macFunction {
type macFunction interface {
Size() int
MAC(digestBuf, seq, header, data []byte) []byte
MAC(digestBuf, seq, header, length, data []byte) []byte
}
// fixedNonceAEAD wraps an AEAD and prefixes a fixed portion of the nonce to
@@ -203,7 +206,7 @@ var ssl30Pad1 = [48]byte{0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0
var ssl30Pad2 = [48]byte{0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c}
func (s ssl30MAC) MAC(digestBuf, seq, header, data []byte) []byte {
func (s ssl30MAC) MAC(digestBuf, seq, header, length, data []byte) []byte {
padLength := 48
if s.h.Size() == 20 {
padLength = 40
@@ -214,7 +217,7 @@ func (s ssl30MAC) MAC(digestBuf, seq, header, data []byte) []byte {
s.h.Write(ssl30Pad1[:padLength])
s.h.Write(seq)
s.h.Write(header[:1])
s.h.Write(header[3:5])
s.h.Write(length)
s.h.Write(data)
digestBuf = s.h.Sum(digestBuf[:0])
@@ -234,10 +237,11 @@ func (s tls10MAC) Size() int {
return s.h.Size()
}
func (s tls10MAC) MAC(digestBuf, seq, header, data []byte) []byte {
func (s tls10MAC) MAC(digestBuf, seq, header, length, data []byte) []byte {
s.h.Reset()
s.h.Write(seq)
s.h.Write(header)
s.h.Write(length)
s.h.Write(data)
return s.h.Sum(digestBuf[:0])
}
+10 -4
View File
@@ -25,10 +25,11 @@ const (
)
const (
maxPlaintext = 16384 // maximum plaintext payload length
maxCiphertext = 16384 + 2048 // maximum ciphertext payload length
recordHeaderLen = 5 // record header length
maxHandshake = 65536 // maximum handshake we support (protocol max is 16 MB)
maxPlaintext = 16384 // maximum plaintext payload length
maxCiphertext = 16384 + 2048 // maximum ciphertext payload length
tlsRecordHeaderLen = 5 // record header length
dtlsRecordHeaderLen = 13
maxHandshake = 65536 // maximum handshake we support (protocol max is 16 MB)
minVersion = VersionSSL30
maxVersion = VersionTLS12
@@ -48,6 +49,7 @@ const (
const (
typeClientHello uint8 = 1
typeServerHello uint8 = 2
typeHelloVerifyRequest uint8 = 3
typeNewSessionTicket uint8 = 4
typeCertificate uint8 = 11
typeServerKeyExchange uint8 = 12
@@ -414,6 +416,10 @@ type ProtocolBugs struct {
// SendClientVersion, if non-zero, causes the client to send a different
// TLS version in the ClientHello than the maximum supported version.
SendClientVersion uint16
// SkipHelloVerifyRequest causes a DTLS server to skip the
// HelloVerifyRequest message.
SkipHelloVerifyRequest bool
}
func (c *Config) serverInit() {
+177 -97
View File
@@ -24,6 +24,7 @@ import (
type Conn struct {
// constant
conn net.Conn
isDTLS bool
isClient bool
// constant after handshake; protected by handshakeMutex
@@ -49,8 +50,14 @@ type Conn struct {
// input/output
in, out halfConn // in.Mutex < out.Mutex
rawInput *block // raw input, right off the wire
input *block // application data waiting to be read
hand bytes.Buffer // handshake data waiting to be read
input *block // application record waiting to be read
hand bytes.Buffer // handshake record waiting to be read
// DTLS state
sendHandshakeSeq uint16
recvHandshakeSeq uint16
handMsg []byte // pending assembled handshake message
handMsgLen int // handshake message length, not including the header
tmp [16]byte
}
@@ -94,8 +101,9 @@ func (c *Conn) SetWriteDeadline(t time.Time) error {
type halfConn struct {
sync.Mutex
err error // first permanent error
version uint16 // protocol version
err error // first permanent error
version uint16 // protocol version
isDTLS bool
cipher interface{} // cipher algorithm
mac macFunction
seq [8]byte // 64-bit sequence number
@@ -141,15 +149,18 @@ func (hc *halfConn) changeCipherSpec(config *Config) error {
hc.nextCipher = nil
hc.nextMac = nil
hc.config = config
for i := range hc.seq {
hc.seq[i] = 0
}
hc.incEpoch()
return nil
}
// incSeq increments the sequence number.
func (hc *halfConn) incSeq() {
for i := 7; i >= 0; i-- {
limit := 0
if hc.isDTLS {
// Increment up to the epoch in DTLS.
limit = 2
}
for i := 7; i >= limit; i-- {
hc.seq[i]++
if hc.seq[i] != 0 {
return
@@ -162,11 +173,33 @@ func (hc *halfConn) incSeq() {
panic("TLS: sequence number wraparound")
}
// resetSeq resets the sequence number to zero.
func (hc *halfConn) resetSeq() {
for i := range hc.seq {
hc.seq[i] = 0
// incEpoch resets the sequence number. In DTLS, it increments the
// epoch half of the sequence number.
func (hc *halfConn) incEpoch() {
limit := 0
if hc.isDTLS {
for i := 1; i >= 0; i-- {
hc.seq[i]++
if hc.seq[i] != 0 {
break
}
if i == 0 {
panic("TLS: epoch number wraparound")
}
}
limit = 2
}
seq := hc.seq[limit:]
for i := range seq {
seq[i] = 0
}
}
func (hc *halfConn) recordHeaderLen() int {
if hc.isDTLS {
return dtlsRecordHeaderLen
}
return tlsRecordHeaderLen
}
// removePadding returns an unpadded slice, in constant time, which is a prefix
@@ -237,6 +270,8 @@ type cbcMode interface {
// success boolean, the number of bytes to skip from the start of the record in
// order to get the application payload, and an optional alert value.
func (hc *halfConn) decrypt(b *block) (ok bool, prefixLen int, alertValue alert) {
recordHeaderLen := hc.recordHeaderLen()
// pull out payload
payload := b.data[recordHeaderLen:]
@@ -248,6 +283,12 @@ func (hc *halfConn) decrypt(b *block) (ok bool, prefixLen int, alertValue alert)
paddingGood := byte(255)
explicitIVLen := 0
seq := hc.seq[:]
if hc.isDTLS {
// DTLS sequence numbers are explicit.
seq = b.data[3:11]
}
// decrypt
if hc.cipher != nil {
switch c := hc.cipher.(type) {
@@ -262,7 +303,7 @@ func (hc *halfConn) decrypt(b *block) (ok bool, prefixLen int, alertValue alert)
payload = payload[8:]
var additionalData [13]byte
copy(additionalData[:], hc.seq[:])
copy(additionalData[:], seq)
copy(additionalData[8:], b.data[:3])
n := len(payload) - c.Overhead()
additionalData[11] = byte(n >> 8)
@@ -275,7 +316,7 @@ func (hc *halfConn) decrypt(b *block) (ok bool, prefixLen int, alertValue alert)
b.resize(recordHeaderLen + explicitIVLen + len(payload))
case cbcMode:
blockSize := c.BlockSize()
if hc.version >= VersionTLS11 {
if hc.version >= VersionTLS11 || hc.isDTLS {
explicitIVLen = blockSize
}
@@ -318,11 +359,11 @@ func (hc *halfConn) decrypt(b *block) (ok bool, prefixLen int, alertValue alert)
// strip mac off payload, b.data
n := len(payload) - macSize
b.data[3] = byte(n >> 8)
b.data[4] = byte(n)
b.data[recordHeaderLen-2] = byte(n >> 8)
b.data[recordHeaderLen-1] = byte(n)
b.resize(recordHeaderLen + explicitIVLen + n)
remoteMAC := payload[n:]
localMAC := hc.mac.MAC(hc.inDigestBuf, hc.seq[0:], b.data[:recordHeaderLen], payload[:n])
localMAC := hc.mac.MAC(hc.inDigestBuf, seq, b.data[:3], b.data[recordHeaderLen-2:recordHeaderLen], payload[:n])
if subtle.ConstantTimeCompare(localMAC, remoteMAC) != 1 || paddingGood != 255 {
return false, 0, alertBadRecordMAC
@@ -364,9 +405,11 @@ func padToBlockSize(payload []byte, blockSize int, config *Config) (prefix, fina
// encrypt encrypts and macs the data in b.
func (hc *halfConn) encrypt(b *block, explicitIVLen int) (bool, alert) {
recordHeaderLen := hc.recordHeaderLen()
// mac
if hc.mac != nil {
mac := hc.mac.MAC(hc.outDigestBuf, hc.seq[0:], b.data[:recordHeaderLen], b.data[recordHeaderLen+explicitIVLen:])
mac := hc.mac.MAC(hc.outDigestBuf, hc.seq[0:], b.data[:3], b.data[recordHeaderLen-2:recordHeaderLen], b.data[recordHeaderLen+explicitIVLen:])
n := len(b.data)
b.resize(n + len(mac))
@@ -412,8 +455,8 @@ func (hc *halfConn) encrypt(b *block, explicitIVLen int) (bool, alert) {
// update length to include MAC and any block padding needed.
n := len(b.data) - recordHeaderLen
b.data[3] = byte(n >> 8)
b.data[4] = byte(n)
b.data[recordHeaderLen-2] = byte(n >> 8)
b.data[recordHeaderLen-1] = byte(n)
hc.incSeq()
return true, 0
@@ -517,6 +560,86 @@ func (hc *halfConn) splitBlock(b *block, n int) (*block, *block) {
return b, bb
}
func (c *Conn) doReadRecord(want recordType) (recordType, *block, error) {
if c.isDTLS {
return c.dtlsDoReadRecord(want)
}
recordHeaderLen := tlsRecordHeaderLen
if c.rawInput == nil {
c.rawInput = c.in.newBlock()
}
b := c.rawInput
// Read header, payload.
if err := b.readFromUntil(c.conn, recordHeaderLen); err != nil {
// RFC suggests that EOF without an alertCloseNotify is
// an error, but popular web sites seem to do this,
// so we can't make it an error.
// if err == io.EOF {
// err = io.ErrUnexpectedEOF
// }
if e, ok := err.(net.Error); !ok || !e.Temporary() {
c.in.setErrorLocked(err)
}
return 0, nil, err
}
typ := recordType(b.data[0])
// No valid TLS record has a type of 0x80, however SSLv2 handshakes
// start with a uint16 length where the MSB is set and the first record
// is always < 256 bytes long. Therefore typ == 0x80 strongly suggests
// an SSLv2 client.
if want == recordTypeHandshake && typ == 0x80 {
c.sendAlert(alertProtocolVersion)
return 0, nil, c.in.setErrorLocked(errors.New("tls: unsupported SSLv2 handshake received"))
}
vers := uint16(b.data[1])<<8 | uint16(b.data[2])
n := int(b.data[3])<<8 | int(b.data[4])
if c.haveVers && vers != c.vers {
c.sendAlert(alertProtocolVersion)
return 0, nil, c.in.setErrorLocked(fmt.Errorf("tls: received record with version %x when expecting version %x", vers, c.vers))
}
if n > maxCiphertext {
c.sendAlert(alertRecordOverflow)
return 0, nil, c.in.setErrorLocked(fmt.Errorf("tls: oversized record received with length %d", n))
}
if !c.haveVers {
// First message, be extra suspicious:
// this might not be a TLS client.
// Bail out before reading a full 'body', if possible.
// The current max version is 3.1.
// If the version is >= 16.0, it's probably not real.
// Similarly, a clientHello message encodes in
// well under a kilobyte. If the length is >= 12 kB,
// it's probably not real.
if (typ != recordTypeAlert && typ != want) || vers >= 0x1000 || n >= 0x3000 {
c.sendAlert(alertUnexpectedMessage)
return 0, nil, c.in.setErrorLocked(fmt.Errorf("tls: first record does not look like a TLS handshake"))
}
}
if err := b.readFromUntil(c.conn, recordHeaderLen+n); err != nil {
if err == io.EOF {
err = io.ErrUnexpectedEOF
}
if e, ok := err.(net.Error); !ok || !e.Temporary() {
c.in.setErrorLocked(err)
}
return 0, nil, err
}
// Process message.
b, c.rawInput = c.in.splitBlock(b, recordHeaderLen+n)
ok, off, err := c.in.decrypt(b)
if !ok {
c.in.setErrorLocked(c.sendAlert(err))
}
b.off = off
return typ, b, nil
}
// readRecord reads the next TLS record from the connection
// and updates the record layer state.
// c.in.Mutex <= L; c.input == nil.
@@ -541,76 +664,10 @@ func (c *Conn) readRecord(want recordType) error {
}
Again:
if c.rawInput == nil {
c.rawInput = c.in.newBlock()
}
b := c.rawInput
// Read header, payload.
if err := b.readFromUntil(c.conn, recordHeaderLen); err != nil {
// RFC suggests that EOF without an alertCloseNotify is
// an error, but popular web sites seem to do this,
// so we can't make it an error.
// if err == io.EOF {
// err = io.ErrUnexpectedEOF
// }
if e, ok := err.(net.Error); !ok || !e.Temporary() {
c.in.setErrorLocked(err)
}
typ, b, err := c.doReadRecord(want)
if err != nil {
return err
}
typ := recordType(b.data[0])
// No valid TLS record has a type of 0x80, however SSLv2 handshakes
// start with a uint16 length where the MSB is set and the first record
// is always < 256 bytes long. Therefore typ == 0x80 strongly suggests
// an SSLv2 client.
if want == recordTypeHandshake && typ == 0x80 {
c.sendAlert(alertProtocolVersion)
return c.in.setErrorLocked(errors.New("tls: unsupported SSLv2 handshake received"))
}
vers := uint16(b.data[1])<<8 | uint16(b.data[2])
n := int(b.data[3])<<8 | int(b.data[4])
if c.haveVers && vers != c.vers {
c.sendAlert(alertProtocolVersion)
return c.in.setErrorLocked(fmt.Errorf("tls: received record with version %x when expecting version %x", vers, c.vers))
}
if n > maxCiphertext {
c.sendAlert(alertRecordOverflow)
return c.in.setErrorLocked(fmt.Errorf("tls: oversized record received with length %d", n))
}
if !c.haveVers {
// First message, be extra suspicious:
// this might not be a TLS client.
// Bail out before reading a full 'body', if possible.
// The current max version is 3.1.
// If the version is >= 16.0, it's probably not real.
// Similarly, a clientHello message encodes in
// well under a kilobyte. If the length is >= 12 kB,
// it's probably not real.
if (typ != recordTypeAlert && typ != want) || vers >= 0x1000 || n >= 0x3000 {
c.sendAlert(alertUnexpectedMessage)
return c.in.setErrorLocked(fmt.Errorf("tls: first record does not look like a TLS handshake"))
}
}
if err := b.readFromUntil(c.conn, recordHeaderLen+n); err != nil {
if err == io.EOF {
err = io.ErrUnexpectedEOF
}
if e, ok := err.(net.Error); !ok || !e.Temporary() {
c.in.setErrorLocked(err)
}
return err
}
// Process message.
b, c.rawInput = c.in.splitBlock(b, recordHeaderLen+n)
ok, off, err := c.in.decrypt(b)
if !ok {
c.in.setErrorLocked(c.sendAlert(err))
}
b.off = off
data := b.data[b.off:]
if len(data) > maxPlaintext {
err := c.sendAlert(alertRecordOverflow)
@@ -713,6 +770,11 @@ func (c *Conn) writeV2Record(data []byte) (n int, err error) {
// to the connection and updates the record layer state.
// c.out.Mutex <= L.
func (c *Conn) writeRecord(typ recordType, data []byte) (n int, err error) {
if c.isDTLS {
return c.dtlsWriteRecord(typ, data)
}
recordHeaderLen := tlsRecordHeaderLen
b := c.out.newBlock()
first := true
isClientHello := typ == recordTypeHandshake && len(data) > 0 && data[0] == typeClientHello
@@ -800,10 +862,11 @@ func (c *Conn) writeRecord(typ recordType, data []byte) (n int, err error) {
return
}
// readHandshake reads the next handshake message from
// the record layer.
// c.in.Mutex < L; c.out.Mutex < L.
func (c *Conn) readHandshake() (interface{}, error) {
func (c *Conn) doReadHandshake() ([]byte, error) {
if c.isDTLS {
return c.dtlsDoReadHandshake()
}
for c.hand.Len() < 4 {
if err := c.in.err; err != nil {
return nil, err
@@ -826,13 +889,28 @@ func (c *Conn) readHandshake() (interface{}, error) {
return nil, err
}
}
data = c.hand.Next(4 + n)
return c.hand.Next(4 + n), nil
}
// readHandshake reads the next handshake message from
// the record layer.
// c.in.Mutex < L; c.out.Mutex < L.
func (c *Conn) readHandshake() (interface{}, error) {
data, err := c.doReadHandshake()
if err != nil {
return nil, err
}
var m handshakeMessage
switch data[0] {
case typeClientHello:
m = new(clientHelloMsg)
m = &clientHelloMsg{
isDTLS: c.isDTLS,
}
case typeServerHello:
m = new(serverHelloMsg)
m = &serverHelloMsg{
isDTLS: c.isDTLS,
}
case typeNewSessionTicket:
m = new(newSessionTicketMsg)
case typeCertificate:
@@ -857,6 +935,8 @@ func (c *Conn) readHandshake() (interface{}, error) {
m = new(nextProtoMsg)
case typeFinished:
m = new(finishedMsg)
case typeHelloVerifyRequest:
m = new(helloVerifyRequestMsg)
default:
return nil, c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage))
}
@@ -899,7 +979,7 @@ func (c *Conn) Write(b []byte) (int, error) {
// http://www.imperialviolet.org/2012/01/15/beastfollowup.html
var m int
if len(b) > 1 && c.vers <= VersionTLS10 {
if len(b) > 1 && c.vers <= VersionTLS10 && !c.isDTLS {
if _, ok := c.out.cipher.(cipher.BlockMode); ok {
n, err := c.writeRecord(recordTypeApplicationData, b[:1])
if err != nil {
@@ -938,7 +1018,7 @@ func (c *Conn) Read(b []byte) (n int, err error) {
}
n, err = c.input.Read(b)
if c.input.off >= len(c.input.data) {
if c.input.off >= len(c.input.data) || c.isDTLS {
c.in.freeBlock(c.input)
c.input = nil
}
+326
View File
@@ -0,0 +1,326 @@
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// DTLS implementation.
//
// NOTE: This is a not even a remotely production-quality DTLS
// implementation. It is the bare minimum necessary to be able to
// achieve coverage on BoringSSL's implementation. Of note is that
// this implementation assumes the underlying net.PacketConn is not
// only reliable but also ordered. BoringSSL will be expected to deal
// with simulated loss, but there is no point in forcing the test
// driver to.
package main
import (
"bytes"
"crypto/cipher"
"errors"
"fmt"
"io"
"net"
)
func versionToWire(vers uint16, isDTLS bool) uint16 {
if isDTLS {
return ^(vers - 0x0201)
}
return vers
}
func wireToVersion(vers uint16, isDTLS bool) uint16 {
if isDTLS {
return ^vers + 0x0201
}
return vers
}
func (c *Conn) dtlsDoReadRecord(want recordType) (recordType, *block, error) {
recordHeaderLen := dtlsRecordHeaderLen
if c.rawInput == nil {
c.rawInput = c.in.newBlock()
}
b := c.rawInput
// Read a new packet only if the current one is empty.
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
}
c.rawInput.resize(n)
}
// Read out one record.
//
// A real DTLS implementation should be tolerant of errors,
// 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")
}
typ := recordType(b.data[0])
vers := wireToVersion(uint16(b.data[1])<<8|uint16(b.data[2]), c.isDTLS)
if c.haveVers && vers != c.vers {
c.sendAlert(alertProtocolVersion)
return 0, nil, c.in.setErrorLocked(fmt.Errorf("dtls: received record with version %x when expecting version %x", vers, c.vers))
}
seq := b.data[3:11]
// For test purposes, we assume a reliable channel. Require
// that the explicit sequence number matches the incrementing
// one we maintain. A real implementation would maintain a
// replay window and such.
if !bytes.Equal(seq, c.in.seq[:]) {
c.sendAlert(alertIllegalParameter)
return 0, nil, c.in.setErrorLocked(fmt.Errorf("dtls: bad sequence number"))
}
n := int(b.data[11])<<8 | int(b.data[12])
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))
}
// Process message.
b, c.rawInput = c.in.splitBlock(b, recordHeaderLen+n)
ok, off, err := c.in.decrypt(b)
if !ok {
c.in.setErrorLocked(c.sendAlert(err))
}
b.off = off
return typ, b, nil
}
func (c *Conn) dtlsWriteRecord(typ recordType, data []byte) (n int, err error) {
recordHeaderLen := dtlsRecordHeaderLen
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:]
}
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]
}
// 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)
// 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:]
}
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 {
// Cannot call sendAlert directly,
// because we already hold c.out.Mutex.
c.tmp[0] = alertLevelError
c.tmp[1] = byte(err.(alert))
c.writeRecord(recordTypeAlert, c.tmp[0:2])
return n, c.out.setErrorLocked(&net.OpError{Op: "local error", Err: err})
}
}
return
}
func (c *Conn) dtlsDoReadHandshake() ([]byte, error) {
// Assemble a full handshake message. For test purposes, this
// implementation assumes fragments arrive in order. It may
// need to be cleverer if we ever test BoringSSL's retransmit
// behavior.
for len(c.handMsg) < 4+c.handMsgLen {
// Get a new handshake record if the previous has been
// exhausted.
if c.hand.Len() == 0 {
if err := c.in.err; err != nil {
return nil, err
}
if err := c.readRecord(recordTypeHandshake); err != nil {
return nil, err
}
}
// Read the next fragment. It must fit entirely within
// the record.
if c.hand.Len() < 12 {
return nil, errors.New("dtls: bad handshake record")
}
header := c.hand.Next(12)
fragN := int(header[1])<<16 | int(header[2])<<8 | int(header[3])
fragSeq := uint16(header[4])<<8 | uint16(header[5])
fragOff := int(header[6])<<16 | int(header[7])<<8 | int(header[8])
fragLen := int(header[9])<<16 | int(header[10])<<8 | int(header[11])
if c.hand.Len() < fragLen {
return nil, errors.New("dtls: fragment length too long")
}
fragment := c.hand.Next(fragLen)
// Check it's a fragment for the right message.
if fragSeq != c.recvHandshakeSeq {
return nil, errors.New("dtls: bad handshake sequence number")
}
// Check that the length is consistent.
if c.handMsg == nil {
c.handMsgLen = fragN
if c.handMsgLen > maxHandshake {
return nil, c.in.setErrorLocked(c.sendAlert(alertInternalError))
}
// Start with the TLS handshake header,
// without the DTLS bits.
c.handMsg = append([]byte{}, header[:4]...)
} else if fragN != c.handMsgLen {
return nil, errors.New("dtls: bad handshake length")
}
// Add the fragment to the pending message.
if 4+fragOff != len(c.handMsg) {
return nil, errors.New("dtls: bad fragment offset")
}
if fragOff+fragLen > c.handMsgLen {
return nil, errors.New("dtls: bad fragment length")
}
c.handMsg = append(c.handMsg, fragment...)
}
c.recvHandshakeSeq++
ret := c.handMsg
c.handMsg, c.handMsgLen = nil, 0
return ret, nil
}
// DTLSServer returns a new DTLS server side connection
// using conn as the underlying transport.
// The configuration config must be non-nil and must have
// at least one certificate.
func DTLSServer(conn net.Conn, config *Config) *Conn {
return &Conn{
config: config,
isDTLS: true,
conn: conn,
in: halfConn{isDTLS: true},
out: halfConn{isDTLS: true},
}
}
// DTLSClient returns a new DTLS client side connection
// using conn as the underlying transport.
// The config cannot be nil: users must set either ServerHostname or
// InsecureSkipVerify in the config.
func DTLSClient(conn net.Conn, config *Config) *Conn {
return &Conn{
config: config,
isClient: true,
isDTLS: true,
conn: conn,
in: halfConn{isDTLS: true},
out: halfConn{isDTLS: true},
}
}
+66 -14
View File
@@ -39,7 +39,11 @@ func (c *Conn) clientHandshake() error {
return errors.New("tls: either ServerName or InsecureSkipVerify must be specified in the tls.Config")
}
c.sendHandshakeSeq = 0
c.recvHandshakeSeq = 0
hello := &clientHelloMsg{
isDTLS: c.isDTLS,
vers: c.config.maxVersion(),
compressionMethods: []uint8{compressionNone},
random: make([]byte, 32),
@@ -70,6 +74,10 @@ NextCipherSuite:
if hello.vers < VersionTLS12 && suite.flags&suiteTLS12 != 0 {
continue
}
// Don't advertise non-DTLS cipher suites on DTLS.
if c.isDTLS && suite.flags&suiteNoDTLS != 0 {
continue
}
hello.cipherSuites = append(hello.cipherSuites, suiteId)
continue NextCipherSuite
}
@@ -154,6 +162,22 @@ NextCipherSuite:
if err != nil {
return err
}
if c.isDTLS {
helloVerifyRequest, ok := msg.(*helloVerifyRequestMsg)
if ok {
hello.raw = nil
hello.cookie = helloVerifyRequest.cookie
helloBytes = hello.marshal()
c.writeRecord(recordTypeHandshake, helloBytes)
msg, err = c.readHandshake()
if err != nil {
return err
}
}
}
serverHello, ok := msg.(*serverHelloMsg)
if !ok {
c.sendAlert(alertUnexpectedMessage)
@@ -184,8 +208,8 @@ NextCipherSuite:
session: session,
}
hs.finishedHash.Write(helloBytes)
hs.finishedHash.Write(hs.serverHello.marshal())
hs.writeHash(helloBytes, hs.c.sendHandshakeSeq-1)
hs.writeServerHash(hs.serverHello.marshal())
if c.config.Bugs.EarlyChangeCipherSpec > 0 {
hs.establishKeys()
@@ -252,7 +276,7 @@ func (hs *clientHandshakeState) doFullHandshake() error {
c.sendAlert(alertUnexpectedMessage)
return unexpectedMessageError(certMsg, msg)
}
hs.finishedHash.Write(certMsg.marshal())
hs.writeServerHash(certMsg.marshal())
certs := make([]*x509.Certificate, len(certMsg.certificates))
for i, asn1Data := range certMsg.certificates {
@@ -305,7 +329,7 @@ func (hs *clientHandshakeState) doFullHandshake() error {
c.sendAlert(alertUnexpectedMessage)
return unexpectedMessageError(cs, msg)
}
hs.finishedHash.Write(cs.marshal())
hs.writeServerHash(cs.marshal())
if cs.statusType == statusTypeOCSP {
c.ocspResponse = cs.response
@@ -321,7 +345,7 @@ func (hs *clientHandshakeState) doFullHandshake() error {
skx, ok := msg.(*serverKeyExchangeMsg)
if ok {
hs.finishedHash.Write(skx.marshal())
hs.writeServerHash(skx.marshal())
err = keyAgreement.processServerKeyExchange(c.config, hs.hello, hs.serverHello, certs[0], skx)
if err != nil {
c.sendAlert(alertUnexpectedMessage)
@@ -351,7 +375,7 @@ func (hs *clientHandshakeState) doFullHandshake() error {
// ClientCertificateType, unless there is some external
// arrangement to the contrary.
hs.finishedHash.Write(certReq.marshal())
hs.writeServerHash(certReq.marshal())
var rsaAvail, ecdsaAvail bool
for _, certType := range certReq.certificateTypes {
@@ -417,7 +441,7 @@ func (hs *clientHandshakeState) doFullHandshake() error {
c.sendAlert(alertUnexpectedMessage)
return unexpectedMessageError(shd, msg)
}
hs.finishedHash.Write(shd.marshal())
hs.writeServerHash(shd.marshal())
// If the server requested a certificate then we have to send a
// Certificate message, even if it's empty because we don't have a
@@ -427,7 +451,7 @@ func (hs *clientHandshakeState) doFullHandshake() error {
if chainToSend != nil {
certMsg.certificates = chainToSend.Certificate
}
hs.finishedHash.Write(certMsg.marshal())
hs.writeClientHash(certMsg.marshal())
c.writeRecord(recordTypeHandshake, certMsg.marshal())
}
@@ -438,7 +462,7 @@ func (hs *clientHandshakeState) doFullHandshake() error {
}
if ckx != nil {
if c.config.Bugs.EarlyChangeCipherSpec < 2 {
hs.finishedHash.Write(ckx.marshal())
hs.writeClientHash(ckx.marshal())
}
c.writeRecord(recordTypeHandshake, ckx.marshal())
}
@@ -486,7 +510,7 @@ func (hs *clientHandshakeState) doFullHandshake() error {
}
certVerify.signature = signed
hs.finishedHash.Write(certVerify.marshal())
hs.writeClientHash(certVerify.marshal())
c.writeRecord(recordTypeHandshake, certVerify.marshal())
}
@@ -571,7 +595,7 @@ func (hs *clientHandshakeState) readFinished() error {
return errors.New("tls: server's Finished message was incorrect")
}
}
hs.finishedHash.Write(serverFinished.marshal())
hs.writeServerHash(serverFinished.marshal())
return nil
}
@@ -590,7 +614,7 @@ func (hs *clientHandshakeState) readSessionTicket() error {
c.sendAlert(alertUnexpectedMessage)
return unexpectedMessageError(sessionTicketMsg, msg)
}
hs.finishedHash.Write(sessionTicketMsg.marshal())
hs.writeServerHash(sessionTicketMsg.marshal())
hs.session = &ClientSessionState{
sessionTicket: sessionTicketMsg.ticket,
@@ -607,6 +631,7 @@ func (hs *clientHandshakeState) sendFinished() error {
c := hs.c
var postCCSBytes []byte
seqno := hs.c.sendHandshakeSeq
if hs.serverHello.nextProtoNeg {
nextProto := new(nextProtoMsg)
proto, fallback := mutualProtocol(c.config.NextProtos, hs.serverHello.nextProtos)
@@ -615,7 +640,8 @@ func (hs *clientHandshakeState) sendFinished() error {
c.clientProtocolFallback = fallback
nextProtoBytes := nextProto.marshal()
hs.finishedHash.Write(nextProtoBytes)
hs.writeHash(nextProtoBytes, seqno)
seqno++
postCCSBytes = append(postCCSBytes, nextProtoBytes...)
}
@@ -626,7 +652,7 @@ func (hs *clientHandshakeState) sendFinished() error {
finished.verifyData = hs.finishedHash.clientSum(hs.masterSecret)
}
finishedBytes := finished.marshal()
hs.finishedHash.Write(finishedBytes)
hs.writeHash(finishedBytes, seqno)
postCCSBytes = append(postCCSBytes, finishedBytes...)
if c.config.Bugs.FragmentAcrossChangeCipherSpec {
@@ -643,6 +669,32 @@ func (hs *clientHandshakeState) sendFinished() error {
return nil
}
func (hs *clientHandshakeState) writeClientHash(msg []byte) {
// writeClientHash is called before writeRecord.
hs.writeHash(msg, hs.c.sendHandshakeSeq)
}
func (hs *clientHandshakeState) writeServerHash(msg []byte) {
// writeServerHash is called after readHandshake.
hs.writeHash(msg, hs.c.recvHandshakeSeq-1)
}
func (hs *clientHandshakeState) writeHash(msg []byte, seqno uint16) {
if hs.c.isDTLS {
// This is somewhat hacky. DTLS hashes a slightly different format.
// First, the TLS header.
hs.finishedHash.Write(msg[:4])
// Then the sequence number and reassembled fragment offset (always 0).
hs.finishedHash.Write([]byte{byte(seqno >> 8), byte(seqno), 0, 0, 0})
// Then the reassembled fragment (always equal to the message length).
hs.finishedHash.Write(msg[1:4])
// And then the message body.
hs.finishedHash.Write(msg[4:])
} else {
hs.finishedHash.Write(msg)
}
}
// clientSessionCacheKey returns a key used to cache sessionTickets that could
// be used to resume previously negotiated TLS sessions with a server.
func clientSessionCacheKey(serverAddr net.Addr, config *Config) string {
+85 -6
View File
@@ -8,9 +8,11 @@ import "bytes"
type clientHelloMsg struct {
raw []byte
isDTLS bool
vers uint16
random []byte
sessionId []byte
cookie []byte
cipherSuites []uint16
compressionMethods []uint8
nextProtoNeg bool
@@ -32,9 +34,11 @@ func (m *clientHelloMsg) equal(i interface{}) bool {
}
return bytes.Equal(m.raw, m1.raw) &&
m.isDTLS == m1.isDTLS &&
m.vers == m1.vers &&
bytes.Equal(m.random, m1.random) &&
bytes.Equal(m.sessionId, m1.sessionId) &&
bytes.Equal(m.cookie, m1.cookie) &&
eqUint16s(m.cipherSuites, m1.cipherSuites) &&
bytes.Equal(m.compressionMethods, m1.compressionMethods) &&
m.nextProtoNeg == m1.nextProtoNeg &&
@@ -54,6 +58,9 @@ func (m *clientHelloMsg) marshal() []byte {
}
length := 2 + 32 + 1 + len(m.sessionId) + 2 + len(m.cipherSuites)*2 + 1 + len(m.compressionMethods)
if m.isDTLS {
length += 1 + len(m.cookie)
}
numExtensions := 0
extensionsLength := 0
if m.nextProtoNeg {
@@ -100,12 +107,18 @@ func (m *clientHelloMsg) marshal() []byte {
x[1] = uint8(length >> 16)
x[2] = uint8(length >> 8)
x[3] = uint8(length)
x[4] = uint8(m.vers >> 8)
x[5] = uint8(m.vers)
vers := versionToWire(m.vers, m.isDTLS)
x[4] = uint8(vers >> 8)
x[5] = uint8(vers)
copy(x[6:38], m.random)
x[38] = uint8(len(m.sessionId))
copy(x[39:39+len(m.sessionId)], m.sessionId)
y := x[39+len(m.sessionId):]
if m.isDTLS {
y[0] = uint8(len(m.cookie))
copy(y[1:], m.cookie)
y = y[1+len(m.cookie):]
}
y[0] = uint8(len(m.cipherSuites) >> 7)
y[1] = uint8(len(m.cipherSuites) << 1)
for i, suite := range m.cipherSuites {
@@ -264,7 +277,7 @@ func (m *clientHelloMsg) unmarshal(data []byte) bool {
return false
}
m.raw = data
m.vers = uint16(data[4])<<8 | uint16(data[5])
m.vers = wireToVersion(uint16(data[4])<<8|uint16(data[5]), m.isDTLS)
m.random = data[6:38]
sessionIdLen := int(data[38])
if sessionIdLen > 32 || len(data) < 39+sessionIdLen {
@@ -272,6 +285,17 @@ func (m *clientHelloMsg) unmarshal(data []byte) bool {
}
m.sessionId = data[39 : 39+sessionIdLen]
data = data[39+sessionIdLen:]
if m.isDTLS {
if len(data) < 1 {
return false
}
cookieLen := int(data[0])
if cookieLen > 32 || len(data) < 1+cookieLen {
return false
}
m.cookie = data[1 : 1+cookieLen]
data = data[1+cookieLen:]
}
if len(data) < 2 {
return false
}
@@ -425,6 +449,7 @@ func (m *clientHelloMsg) unmarshal(data []byte) bool {
type serverHelloMsg struct {
raw []byte
isDTLS bool
vers uint16
random []byte
sessionId []byte
@@ -445,6 +470,7 @@ func (m *serverHelloMsg) equal(i interface{}) bool {
}
return bytes.Equal(m.raw, m1.raw) &&
m.isDTLS == m1.isDTLS &&
m.vers == m1.vers &&
bytes.Equal(m.random, m1.random) &&
bytes.Equal(m.sessionId, m1.sessionId) &&
@@ -498,8 +524,9 @@ func (m *serverHelloMsg) marshal() []byte {
x[1] = uint8(length >> 16)
x[2] = uint8(length >> 8)
x[3] = uint8(length)
x[4] = uint8(m.vers >> 8)
x[5] = uint8(m.vers)
vers := versionToWire(m.vers, m.isDTLS)
x[4] = uint8(vers >> 8)
x[5] = uint8(vers)
copy(x[6:38], m.random)
x[38] = uint8(len(m.sessionId))
copy(x[39:39+len(m.sessionId)], m.sessionId)
@@ -571,7 +598,7 @@ func (m *serverHelloMsg) unmarshal(data []byte) bool {
return false
}
m.raw = data
m.vers = uint16(data[4])<<8 | uint16(data[5])
m.vers = wireToVersion(uint16(data[4])<<8|uint16(data[5]), m.isDTLS)
m.random = data[6:38]
sessionIdLen := int(data[38])
if sessionIdLen > 32 || len(data) < 39+sessionIdLen {
@@ -1368,6 +1395,58 @@ func (m *v2ClientHelloMsg) marshal() []byte {
return x
}
type helloVerifyRequestMsg struct {
raw []byte
vers uint16
cookie []byte
}
func (m *helloVerifyRequestMsg) equal(i interface{}) bool {
m1, ok := i.(*helloVerifyRequestMsg)
if !ok {
return false
}
return m.vers == m1.vers &&
bytes.Equal(m.cookie, m1.cookie)
}
func (m *helloVerifyRequestMsg) marshal() []byte {
if m.raw != nil {
return m.raw
}
length := 2 + 1 + len(m.cookie)
x := make([]byte, 4+length)
x[0] = typeHelloVerifyRequest
x[1] = uint8(length >> 16)
x[2] = uint8(length >> 8)
x[3] = uint8(length)
vers := versionToWire(m.vers, true)
x[4] = uint8(vers >> 8)
x[5] = uint8(vers)
x[6] = uint8(len(m.cookie))
copy(x[7:7+len(m.cookie)], m.cookie)
return x
}
func (m *helloVerifyRequestMsg) unmarshal(data []byte) bool {
if len(data) < 4+2+1 {
return false
}
m.raw = data
m.vers = wireToVersion(uint16(data[4])<<8|uint16(data[5]), true)
cookieLen := int(data[6])
if cookieLen > 32 || len(data) != 7+cookieLen {
return false
}
m.cookie = data[7 : 7+cookieLen]
return true
}
func eqUint16s(x, y []uint16) bool {
if len(x) != len(y) {
return false
+84 -16
View File
@@ -5,6 +5,7 @@
package main
import (
"bytes"
"crypto"
"crypto/ecdsa"
"crypto/rsa"
@@ -40,6 +41,9 @@ func (c *Conn) serverHandshake() error {
// encrypt the tickets with.
config.serverInitOnce.Do(config.serverInit)
c.sendHandshakeSeq = 0
c.recvHandshakeSeq = 0
hs := serverHandshakeState{
c: c,
}
@@ -114,9 +118,44 @@ func (hs *serverHandshakeState) readClientHello() (isResume bool, err error) {
c.sendAlert(alertProtocolVersion)
return false, fmt.Errorf("tls: client offered an unsupported, maximum protocol version of %x", hs.clientHello.vers)
}
if c.isDTLS && !config.Bugs.SkipHelloVerifyRequest {
helloVerifyRequest := &helloVerifyRequestMsg{
vers: c.vers,
cookie: make([]byte, 32),
}
if _, err := io.ReadFull(c.config.rand(), helloVerifyRequest.cookie); err != nil {
c.sendAlert(alertInternalError)
return false, errors.New("dtls: short read from Rand: " + err.Error())
}
c.writeRecord(recordTypeHandshake, helloVerifyRequest.marshal())
msg, err := c.readHandshake()
if err != nil {
return false, err
}
newClientHello, ok := msg.(*clientHelloMsg)
if !ok {
c.sendAlert(alertUnexpectedMessage)
return false, unexpectedMessageError(hs.clientHello, msg)
}
if !bytes.Equal(newClientHello.cookie, helloVerifyRequest.cookie) {
return false, errors.New("dtls: invalid cookie")
}
// Apart from the cookie, client hello must match.
hs.clientHello.cookie = newClientHello.cookie
if hs.clientHello.equal(newClientHello) {
return false, errors.New("dtls: retransmitted ClientHello does not match")
}
hs.clientHello = newClientHello
}
// Do not set c.haveVers until after HelloVerifyRequest; the
// retransmitted ClientHello may not have the final version.
c.haveVers = true
hs.hello = new(serverHelloMsg)
hs.hello.isDTLS = c.isDTLS
supportedCurve := false
preferredCurves := config.curvePreferences()
@@ -284,8 +323,8 @@ func (hs *serverHandshakeState) doResumeHandshake() error {
hs.hello.ticketSupported = c.config.Bugs.RenewTicketOnResume
hs.finishedHash = newFinishedHash(c.vers, hs.suite)
hs.finishedHash.Write(hs.clientHello.marshal())
hs.finishedHash.Write(hs.hello.marshal())
hs.writeClientHash(hs.clientHello.marshal())
hs.writeServerHash(hs.hello.marshal())
c.writeRecord(recordTypeHandshake, hs.hello.marshal())
@@ -312,15 +351,15 @@ func (hs *serverHandshakeState) doFullHandshake() error {
hs.hello.cipherSuite = hs.suite.id
hs.finishedHash = newFinishedHash(c.vers, hs.suite)
hs.finishedHash.Write(hs.clientHello.marshal())
hs.finishedHash.Write(hs.hello.marshal())
hs.writeClientHash(hs.clientHello.marshal())
hs.writeServerHash(hs.hello.marshal())
c.writeRecord(recordTypeHandshake, hs.hello.marshal())
certMsg := new(certificateMsg)
certMsg.certificates = hs.cert.Certificate
if !config.Bugs.UnauthenticatedECDH {
hs.finishedHash.Write(certMsg.marshal())
hs.writeServerHash(certMsg.marshal())
c.writeRecord(recordTypeHandshake, certMsg.marshal())
}
@@ -328,7 +367,7 @@ func (hs *serverHandshakeState) doFullHandshake() error {
certStatus := new(certificateStatusMsg)
certStatus.statusType = statusTypeOCSP
certStatus.response = hs.cert.OCSPStaple
hs.finishedHash.Write(certStatus.marshal())
hs.writeServerHash(certStatus.marshal())
c.writeRecord(recordTypeHandshake, certStatus.marshal())
}
@@ -339,7 +378,7 @@ func (hs *serverHandshakeState) doFullHandshake() error {
return err
}
if skx != nil && !config.Bugs.SkipServerKeyExchange {
hs.finishedHash.Write(skx.marshal())
hs.writeServerHash(skx.marshal())
c.writeRecord(recordTypeHandshake, skx.marshal())
}
@@ -367,12 +406,12 @@ func (hs *serverHandshakeState) doFullHandshake() error {
if config.ClientCAs != nil {
certReq.certificateAuthorities = config.ClientCAs.Subjects()
}
hs.finishedHash.Write(certReq.marshal())
hs.writeServerHash(certReq.marshal())
c.writeRecord(recordTypeHandshake, certReq.marshal())
}
helloDone := new(serverHelloDoneMsg)
hs.finishedHash.Write(helloDone.marshal())
hs.writeServerHash(helloDone.marshal())
c.writeRecord(recordTypeHandshake, helloDone.marshal())
var pub crypto.PublicKey // public key for client auth, if any
@@ -390,7 +429,7 @@ func (hs *serverHandshakeState) doFullHandshake() error {
c.sendAlert(alertUnexpectedMessage)
return unexpectedMessageError(certMsg, msg)
}
hs.finishedHash.Write(certMsg.marshal())
hs.writeClientHash(certMsg.marshal())
if len(certMsg.certificates) == 0 {
// The client didn't actually send a certificate
@@ -418,7 +457,7 @@ func (hs *serverHandshakeState) doFullHandshake() error {
c.sendAlert(alertUnexpectedMessage)
return unexpectedMessageError(ckx, msg)
}
hs.finishedHash.Write(ckx.marshal())
hs.writeClientHash(ckx.marshal())
// If we received a client cert in response to our certificate request message,
// the client will send us a certificateVerifyMsg immediately after the
@@ -494,7 +533,7 @@ func (hs *serverHandshakeState) doFullHandshake() error {
return errors.New("could not validate signature of connection nonces: " + err.Error())
}
hs.finishedHash.Write(certVerify.marshal())
hs.writeClientHash(certVerify.marshal())
}
preMasterSecret, err := keyAgreement.processClientKeyExchange(config, hs.cert, ckx, c.vers)
@@ -550,7 +589,7 @@ func (hs *serverHandshakeState) readFinished() error {
c.sendAlert(alertUnexpectedMessage)
return unexpectedMessageError(nextProto, msg)
}
hs.finishedHash.Write(nextProto.marshal())
hs.writeClientHash(nextProto.marshal())
c.clientProtocol = nextProto.proto
}
@@ -571,7 +610,7 @@ func (hs *serverHandshakeState) readFinished() error {
return errors.New("tls: client's Finished message is incorrect")
}
hs.finishedHash.Write(clientFinished.marshal())
hs.writeClientHash(clientFinished.marshal())
return nil
}
@@ -595,7 +634,7 @@ func (hs *serverHandshakeState) sendSessionTicket() error {
return err
}
hs.finishedHash.Write(m.marshal())
hs.writeServerHash(m.marshal())
c.writeRecord(recordTypeHandshake, m.marshal())
return nil
@@ -607,7 +646,7 @@ func (hs *serverHandshakeState) sendFinished() error {
finished := new(finishedMsg)
finished.verifyData = hs.finishedHash.serverSum(hs.masterSecret)
postCCSBytes := finished.marshal()
hs.finishedHash.Write(postCCSBytes)
hs.writeServerHash(postCCSBytes)
if c.config.Bugs.FragmentAcrossChangeCipherSpec {
c.writeRecord(recordTypeHandshake, postCCSBytes[:5])
@@ -690,6 +729,32 @@ func (hs *serverHandshakeState) processCertsFromClient(certificates [][]byte) (c
return nil, nil
}
func (hs *serverHandshakeState) writeServerHash(msg []byte) {
// writeServerHash is called before writeRecord.
hs.writeHash(msg, hs.c.sendHandshakeSeq)
}
func (hs *serverHandshakeState) writeClientHash(msg []byte) {
// writeClientHash is called after readHandshake.
hs.writeHash(msg, hs.c.recvHandshakeSeq-1)
}
func (hs *serverHandshakeState) writeHash(msg []byte, seqno uint16) {
if hs.c.isDTLS {
// This is somewhat hacky. DTLS hashes a slightly different format.
// First, the TLS header.
hs.finishedHash.Write(msg[:4])
// Then the sequence number and reassembled fragment offset (always 0).
hs.finishedHash.Write([]byte{byte(seqno >> 8), byte(seqno), 0, 0, 0})
// Then the reassembled fragment (always equal to the message length).
hs.finishedHash.Write(msg[1:4])
// And then the message body.
hs.finishedHash.Write(msg[4:])
} else {
hs.finishedHash.Write(msg)
}
}
// tryCipherSuite returns a cipherSuite with the given id if that cipher suite
// is acceptable to use.
func (c *Conn) tryCipherSuite(id uint16, supportedCipherSuites []uint16, version uint16, ellipticOk, ecdsaOk bool) *cipherSuite {
@@ -717,6 +782,9 @@ func (c *Conn) tryCipherSuite(id uint16, supportedCipherSuites []uint16, version
if version < VersionTLS12 && candidate.flags&suiteTLS12 != 0 {
continue
}
if c.isDTLS && candidate.flags&suiteNoDTLS != 0 {
continue
}
return candidate
}
}
+1
View File
@@ -74,6 +74,7 @@ func (ka rsaKeyAgreement) generateClientKeyExchange(config *Config, clientHello
if config.Bugs.RsaClientKeyExchangeVersion != 0 {
vers = config.Bugs.RsaClientKeyExchangeVersion
}
vers = versionToWire(vers, clientHello.isDTLS)
preMasterSecret[0] = byte(vers >> 8)
preMasterSecret[1] = byte(vers)
_, err := io.ReadFull(config.rand(), preMasterSecret[2:])