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:
committed by
Adam Langley
parent
51e3283d62
commit
83c0bc94d7
@@ -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])
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
|
||||
}
|
||||
|
||||
@@ -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},
|
||||
}
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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:])
|
||||
|
||||
Reference in New Issue
Block a user