|  | // 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 runner | 
|  |  | 
|  | import ( | 
|  | "bytes" | 
|  | "errors" | 
|  | "fmt" | 
|  | "io" | 
|  | "math/rand" | 
|  | "net" | 
|  | ) | 
|  |  | 
|  | 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. | 
|  | var newPacket bool | 
|  | 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 | 
|  | } | 
|  | if c.config.Bugs.MaxPacketLength != 0 && n > c.config.Bugs.MaxPacketLength { | 
|  | return 0, nil, fmt.Errorf("dtls: exceeded maximum packet length") | 
|  | } | 
|  | c.rawInput.resize(n) | 
|  | newPacket = true | 
|  | } | 
|  |  | 
|  | // 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 := uint16(b.data[1])<<8 | uint16(b.data[2]) | 
|  | // Alerts sent near version negotiation do not have a well-defined | 
|  | // record-layer version prior to TLS 1.3. (In TLS 1.3, the record-layer | 
|  | // version is irrelevant.) | 
|  | if typ != recordTypeAlert { | 
|  | if c.haveVers { | 
|  | if vers != c.wireVersion { | 
|  | c.sendAlert(alertProtocolVersion) | 
|  | return 0, nil, c.in.setErrorLocked(fmt.Errorf("dtls: received record with version %x when expecting version %x", vers, c.wireVersion)) | 
|  | } | 
|  | } else { | 
|  | // Pre-version-negotiation alerts may be sent with any version. | 
|  | if expect := c.config.Bugs.ExpectInitialRecordVersion; expect != 0 && vers != expect { | 
|  | c.sendAlert(alertProtocolVersion) | 
|  | return 0, nil, c.in.setErrorLocked(fmt.Errorf("dtls: received record with version %x when expecting version %x", vers, expect)) | 
|  | } | 
|  | } | 
|  | } | 
|  | epoch := b.data[3:5] | 
|  | seq := b.data[5:11] | 
|  | // For test purposes, require the sequence number be monotonically | 
|  | // increasing, so c.in includes the minimum next sequence number. Gaps | 
|  | // may occur if packets failed to be sent out. A real implementation | 
|  | // would maintain a replay window and such. | 
|  | if !bytes.Equal(epoch, c.in.seq[:2]) { | 
|  | c.sendAlert(alertIllegalParameter) | 
|  | return 0, nil, c.in.setErrorLocked(fmt.Errorf("dtls: bad epoch")) | 
|  | } | 
|  | if bytes.Compare(seq, c.in.seq[2:]) < 0 { | 
|  | c.sendAlert(alertIllegalParameter) | 
|  | return 0, nil, c.in.setErrorLocked(fmt.Errorf("dtls: bad sequence number")) | 
|  | } | 
|  | copy(c.in.seq[2:], seq) | 
|  | 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, _, alertValue := c.in.decrypt(b) | 
|  | if !ok { | 
|  | // A real DTLS implementation would silently ignore bad records, | 
|  | // but we want to notice errors from the implementation under | 
|  | // test. | 
|  | return 0, nil, c.in.setErrorLocked(c.sendAlert(alertValue)) | 
|  | } | 
|  | b.off = off | 
|  |  | 
|  | // TODO(nharper): Once DTLS 1.3 is defined, handle the extra | 
|  | // parameter from decrypt. | 
|  |  | 
|  | // Require that ChangeCipherSpec always share a packet with either the | 
|  | // previous or next handshake message. | 
|  | if newPacket && typ == recordTypeChangeCipherSpec && c.rawInput == nil { | 
|  | return 0, nil, c.in.setErrorLocked(fmt.Errorf("dtls: ChangeCipherSpec not packed together with Finished")) | 
|  | } | 
|  |  | 
|  | return typ, b, nil | 
|  | } | 
|  |  | 
|  | func (c *Conn) makeFragment(header, data []byte, fragOffset, fragLen int) []byte { | 
|  | fragment := make([]byte, 0, 12+fragLen) | 
|  | fragment = append(fragment, header...) | 
|  | fragment = append(fragment, byte(c.sendHandshakeSeq>>8), byte(c.sendHandshakeSeq)) | 
|  | fragment = append(fragment, byte(fragOffset>>16), byte(fragOffset>>8), byte(fragOffset)) | 
|  | fragment = append(fragment, byte(fragLen>>16), byte(fragLen>>8), byte(fragLen)) | 
|  | fragment = append(fragment, data[fragOffset:fragOffset+fragLen]...) | 
|  | return fragment | 
|  | } | 
|  |  | 
|  | func (c *Conn) dtlsWriteRecord(typ recordType, data []byte) (n int, err error) { | 
|  | // Only handshake messages are fragmented. | 
|  | if typ != recordTypeHandshake { | 
|  | reorder := typ == recordTypeChangeCipherSpec && c.config.Bugs.ReorderChangeCipherSpec | 
|  |  | 
|  | // Flush pending handshake messages before encrypting a new record. | 
|  | if !reorder { | 
|  | err = c.dtlsPackHandshake() | 
|  | if err != nil { | 
|  | return | 
|  | } | 
|  | } | 
|  |  | 
|  | if typ == recordTypeApplicationData && len(data) > 1 && c.config.Bugs.SplitAndPackAppData { | 
|  | _, err = c.dtlsPackRecord(typ, data[:len(data)/2], false) | 
|  | if err != nil { | 
|  | return | 
|  | } | 
|  | _, err = c.dtlsPackRecord(typ, data[len(data)/2:], true) | 
|  | if err != nil { | 
|  | return | 
|  | } | 
|  | n = len(data) | 
|  | } else { | 
|  | n, err = c.dtlsPackRecord(typ, data, false) | 
|  | if err != nil { | 
|  | return | 
|  | } | 
|  | } | 
|  |  | 
|  | if reorder { | 
|  | err = c.dtlsPackHandshake() | 
|  | if err != nil { | 
|  | return | 
|  | } | 
|  | } | 
|  |  | 
|  | if typ == recordTypeChangeCipherSpec { | 
|  | err = c.out.changeCipherSpec(c.config) | 
|  | if err != nil { | 
|  | return n, c.sendAlertLocked(alertLevelError, err.(alert)) | 
|  | } | 
|  | } else { | 
|  | // ChangeCipherSpec is part of the handshake and not | 
|  | // flushed until dtlsFlushPacket. | 
|  | err = c.dtlsFlushPacket() | 
|  | if err != nil { | 
|  | return | 
|  | } | 
|  | } | 
|  | return | 
|  | } | 
|  |  | 
|  | if c.out.cipher == nil && c.config.Bugs.StrayChangeCipherSpec { | 
|  | _, err = c.dtlsPackRecord(recordTypeChangeCipherSpec, []byte{1}, false) | 
|  | if err != nil { | 
|  | return | 
|  | } | 
|  | } | 
|  |  | 
|  | maxLen := c.config.Bugs.MaxHandshakeRecordLength | 
|  | if maxLen <= 0 { | 
|  | maxLen = 1024 | 
|  | } | 
|  |  | 
|  | // Handshake messages have to be modified to include fragment | 
|  | // offset and length and with the header replicated. Save the | 
|  | // TLS header here. | 
|  | // | 
|  | // TODO(davidben): This assumes that data contains exactly one | 
|  | // handshake message. This is incompatible with | 
|  | // FragmentAcrossChangeCipherSpec. (Which is unfortunate | 
|  | // because OpenSSL's DTLS implementation will probably accept | 
|  | // such fragmentation and could do with a fix + tests.) | 
|  | header := data[:4] | 
|  | data = data[4:] | 
|  |  | 
|  | isFinished := header[0] == typeFinished | 
|  |  | 
|  | if c.config.Bugs.SendEmptyFragments { | 
|  | c.pendingFragments = append(c.pendingFragments, c.makeFragment(header, data, 0, 0)) | 
|  | c.pendingFragments = append(c.pendingFragments, c.makeFragment(header, data, len(data), 0)) | 
|  | } | 
|  |  | 
|  | firstRun := true | 
|  | fragOffset := 0 | 
|  | for firstRun || fragOffset < len(data) { | 
|  | firstRun = false | 
|  | fragLen := len(data) - fragOffset | 
|  | if fragLen > maxLen { | 
|  | fragLen = maxLen | 
|  | } | 
|  |  | 
|  | fragment := c.makeFragment(header, data, fragOffset, fragLen) | 
|  | if c.config.Bugs.FragmentMessageTypeMismatch && fragOffset > 0 { | 
|  | fragment[0]++ | 
|  | } | 
|  | if c.config.Bugs.FragmentMessageLengthMismatch && fragOffset > 0 { | 
|  | fragment[3]++ | 
|  | } | 
|  |  | 
|  | // Buffer the fragment for later. They will be sent (and | 
|  | // reordered) on flush. | 
|  | c.pendingFragments = append(c.pendingFragments, fragment) | 
|  | if c.config.Bugs.ReorderHandshakeFragments { | 
|  | // Don't duplicate Finished to avoid the peer | 
|  | // interpreting it as a retransmit request. | 
|  | if !isFinished { | 
|  | c.pendingFragments = append(c.pendingFragments, fragment) | 
|  | } | 
|  |  | 
|  | if fragLen > (maxLen+1)/2 { | 
|  | // Overlap each fragment by half. | 
|  | fragLen = (maxLen + 1) / 2 | 
|  | } | 
|  | } | 
|  | fragOffset += fragLen | 
|  | n += fragLen | 
|  | } | 
|  | shouldSendTwice := c.config.Bugs.MixCompleteMessageWithFragments | 
|  | if isFinished { | 
|  | shouldSendTwice = c.config.Bugs.RetransmitFinished | 
|  | } | 
|  | if shouldSendTwice { | 
|  | fragment := c.makeFragment(header, data, 0, len(data)) | 
|  | c.pendingFragments = append(c.pendingFragments, fragment) | 
|  | } | 
|  |  | 
|  | // Increment the handshake sequence number for the next | 
|  | // handshake message. | 
|  | c.sendHandshakeSeq++ | 
|  | return | 
|  | } | 
|  |  | 
|  | // dtlsPackHandshake packs the pending handshake flight into the pending | 
|  | // record. Callers should follow up with dtlsFlushPacket to write the packets. | 
|  | func (c *Conn) dtlsPackHandshake() error { | 
|  | // This is a test-only DTLS implementation, so there is no need to | 
|  | // retain |c.pendingFragments| for a future retransmit. | 
|  | var fragments [][]byte | 
|  | fragments, c.pendingFragments = c.pendingFragments, fragments | 
|  |  | 
|  | if c.config.Bugs.ReorderHandshakeFragments { | 
|  | perm := rand.New(rand.NewSource(0)).Perm(len(fragments)) | 
|  | tmp := make([][]byte, len(fragments)) | 
|  | for i := range tmp { | 
|  | tmp[i] = fragments[perm[i]] | 
|  | } | 
|  | fragments = tmp | 
|  | } else if c.config.Bugs.ReverseHandshakeFragments { | 
|  | tmp := make([][]byte, len(fragments)) | 
|  | for i := range tmp { | 
|  | tmp[i] = fragments[len(fragments)-i-1] | 
|  | } | 
|  | fragments = tmp | 
|  | } | 
|  |  | 
|  | maxRecordLen := c.config.Bugs.PackHandshakeFragments | 
|  |  | 
|  | // Pack handshake fragments into records. | 
|  | var records [][]byte | 
|  | for _, fragment := range fragments { | 
|  | if n := c.config.Bugs.SplitFragments; n > 0 { | 
|  | if len(fragment) > n { | 
|  | records = append(records, fragment[:n]) | 
|  | records = append(records, fragment[n:]) | 
|  | } else { | 
|  | records = append(records, fragment) | 
|  | } | 
|  | } else if i := len(records) - 1; len(records) > 0 && len(records[i])+len(fragment) <= maxRecordLen { | 
|  | records[i] = append(records[i], fragment...) | 
|  | } else { | 
|  | // The fragment will be appended to, so copy it. | 
|  | records = append(records, append([]byte{}, fragment...)) | 
|  | } | 
|  | } | 
|  |  | 
|  | // Send the records. | 
|  | for _, record := range records { | 
|  | _, err := c.dtlsPackRecord(recordTypeHandshake, record, false) | 
|  | if err != nil { | 
|  | return err | 
|  | } | 
|  | } | 
|  |  | 
|  | return nil | 
|  | } | 
|  |  | 
|  | func (c *Conn) dtlsFlushHandshake() error { | 
|  | if err := c.dtlsPackHandshake(); err != nil { | 
|  | return err | 
|  | } | 
|  | if err := c.dtlsFlushPacket(); err != nil { | 
|  | return err | 
|  | } | 
|  |  | 
|  | return nil | 
|  | } | 
|  |  | 
|  | // dtlsPackRecord packs a single record to the pending packet, flushing it | 
|  | // if necessary. The caller should call dtlsFlushPacket to flush the current | 
|  | // pending packet afterwards. | 
|  | func (c *Conn) dtlsPackRecord(typ recordType, data []byte, mustPack bool) (n int, err error) { | 
|  | recordHeaderLen := dtlsRecordHeaderLen | 
|  | maxLen := c.config.Bugs.MaxHandshakeRecordLength | 
|  | if maxLen <= 0 { | 
|  | maxLen = 1024 | 
|  | } | 
|  |  | 
|  | b := c.out.newBlock() | 
|  |  | 
|  | explicitIVLen := 0 | 
|  | explicitIVIsSeq := false | 
|  |  | 
|  | if cbc, ok := c.out.cipher.(cbcMode); ok { | 
|  | // Block cipher modes have an explicit IV. | 
|  | explicitIVLen = cbc.BlockSize() | 
|  | } else if aead, ok := c.out.cipher.(*tlsAead); ok { | 
|  | if aead.explicitNonce { | 
|  | 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 _, ok := c.out.cipher.(nullCipher); !ok && c.out.cipher != nil { | 
|  | panic("Unknown cipher") | 
|  | } | 
|  | b.resize(recordHeaderLen + explicitIVLen + len(data)) | 
|  | // TODO(nharper): DTLS 1.3 will likely need to set this to | 
|  | // recordTypeApplicationData if c.out.cipher != nil. | 
|  | b.data[0] = byte(typ) | 
|  | vers := c.wireVersion | 
|  | if vers == 0 { | 
|  | // Some TLS servers fail if the record version is greater than | 
|  | // TLS 1.0 for the initial ClientHello. | 
|  | if c.isDTLS { | 
|  | vers = VersionDTLS10 | 
|  | } else { | 
|  | vers = VersionTLS10 | 
|  | } | 
|  | } | 
|  | 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.outSeq[0:]) | 
|  | b.data[11] = byte(len(data) >> 8) | 
|  | b.data[12] = byte(len(data)) | 
|  | if explicitIVLen > 0 { | 
|  | explicitIV := b.data[recordHeaderLen : recordHeaderLen+explicitIVLen] | 
|  | if explicitIVIsSeq { | 
|  | copy(explicitIV, c.out.outSeq[:]) | 
|  | } else { | 
|  | if _, err = io.ReadFull(c.config.rand(), explicitIV); err != nil { | 
|  | return | 
|  | } | 
|  | } | 
|  | } | 
|  | copy(b.data[recordHeaderLen+explicitIVLen:], data) | 
|  | c.out.encrypt(b, explicitIVLen, typ) | 
|  |  | 
|  | // Flush the current pending packet if necessary. | 
|  | if !mustPack && len(b.data)+len(c.pendingPacket) > c.config.Bugs.PackHandshakeRecords { | 
|  | err = c.dtlsFlushPacket() | 
|  | if err != nil { | 
|  | c.out.freeBlock(b) | 
|  | return | 
|  | } | 
|  | } | 
|  |  | 
|  | // Add the record to the pending packet. | 
|  | c.pendingPacket = append(c.pendingPacket, b.data...) | 
|  | c.out.freeBlock(b) | 
|  | n = len(data) | 
|  | return | 
|  | } | 
|  |  | 
|  | func (c *Conn) dtlsFlushPacket() error { | 
|  | if len(c.pendingPacket) == 0 { | 
|  | return nil | 
|  | } | 
|  | _, err := c.conn.Write(c.pendingPacket) | 
|  | c.pendingPacket = nil | 
|  | return err | 
|  | } | 
|  |  | 
|  | 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 { | 
|  | c := &Conn{config: config, isDTLS: true, conn: conn} | 
|  | c.init() | 
|  | return c | 
|  | } | 
|  |  | 
|  | // 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 { | 
|  | c := &Conn{config: config, isClient: true, isDTLS: true, conn: conn} | 
|  | c.init() | 
|  | return c | 
|  | } |