David Benjamin | 83c0bc9 | 2014-08-04 01:23:53 -0400 | [diff] [blame] | 1 | // Copyright 2014 The Go Authors. All rights reserved. |
| 2 | // Use of this source code is governed by a BSD-style |
| 3 | // license that can be found in the LICENSE file. |
| 4 | |
| 5 | // DTLS implementation. |
| 6 | // |
| 7 | // NOTE: This is a not even a remotely production-quality DTLS |
| 8 | // implementation. It is the bare minimum necessary to be able to |
| 9 | // achieve coverage on BoringSSL's implementation. Of note is that |
| 10 | // this implementation assumes the underlying net.PacketConn is not |
| 11 | // only reliable but also ordered. BoringSSL will be expected to deal |
| 12 | // with simulated loss, but there is no point in forcing the test |
| 13 | // driver to. |
| 14 | |
Adam Langley | dc7e9c4 | 2015-09-29 15:21:04 -0700 | [diff] [blame] | 15 | package runner |
David Benjamin | 83c0bc9 | 2014-08-04 01:23:53 -0400 | [diff] [blame] | 16 | |
| 17 | import ( |
| 18 | "bytes" |
David Benjamin | 83c0bc9 | 2014-08-04 01:23:53 -0400 | [diff] [blame] | 19 | "errors" |
| 20 | "fmt" |
| 21 | "io" |
David Benjamin | b3774b9 | 2015-01-31 17:16:01 -0500 | [diff] [blame] | 22 | "math/rand" |
David Benjamin | 83c0bc9 | 2014-08-04 01:23:53 -0400 | [diff] [blame] | 23 | "net" |
| 24 | ) |
| 25 | |
David Benjamin | b1dd8cd | 2016-09-26 19:20:48 -0400 | [diff] [blame] | 26 | func wireToVersion(vers uint16, isDTLS bool) (uint16, bool) { |
David Benjamin | 83c0bc9 | 2014-08-04 01:23:53 -0400 | [diff] [blame] | 27 | if isDTLS { |
David Benjamin | b1dd8cd | 2016-09-26 19:20:48 -0400 | [diff] [blame] | 28 | switch vers { |
Steven Valdez | c94998a | 2017-06-20 10:55:02 -0400 | [diff] [blame] | 29 | case VersionDTLS12: |
David Benjamin | b1dd8cd | 2016-09-26 19:20:48 -0400 | [diff] [blame] | 30 | return VersionTLS12, true |
Steven Valdez | c94998a | 2017-06-20 10:55:02 -0400 | [diff] [blame] | 31 | case VersionDTLS10: |
David Benjamin | b1dd8cd | 2016-09-26 19:20:48 -0400 | [diff] [blame] | 32 | return VersionTLS10, true |
| 33 | } |
| 34 | } else { |
| 35 | switch vers { |
Steven Valdez | fdd1099 | 2016-09-15 16:27:05 -0400 | [diff] [blame] | 36 | case VersionSSL30, VersionTLS10, VersionTLS11, VersionTLS12: |
David Benjamin | b1dd8cd | 2016-09-26 19:20:48 -0400 | [diff] [blame] | 37 | return vers, true |
Steven Valdez | dbe0158 | 2017-07-14 10:39:28 -0400 | [diff] [blame] | 38 | case tls13DraftVersion, tls13ExperimentVersion, tls13RecordTypeExperimentVersion: |
Steven Valdez | fdd1099 | 2016-09-15 16:27:05 -0400 | [diff] [blame] | 39 | return VersionTLS13, true |
David Benjamin | b1dd8cd | 2016-09-26 19:20:48 -0400 | [diff] [blame] | 40 | } |
David Benjamin | 83c0bc9 | 2014-08-04 01:23:53 -0400 | [diff] [blame] | 41 | } |
David Benjamin | b1dd8cd | 2016-09-26 19:20:48 -0400 | [diff] [blame] | 42 | |
| 43 | return 0, false |
David Benjamin | 83c0bc9 | 2014-08-04 01:23:53 -0400 | [diff] [blame] | 44 | } |
| 45 | |
| 46 | func (c *Conn) dtlsDoReadRecord(want recordType) (recordType, *block, error) { |
| 47 | recordHeaderLen := dtlsRecordHeaderLen |
| 48 | |
| 49 | if c.rawInput == nil { |
| 50 | c.rawInput = c.in.newBlock() |
| 51 | } |
| 52 | b := c.rawInput |
| 53 | |
| 54 | // Read a new packet only if the current one is empty. |
David Benjamin | 30152fd | 2016-05-05 20:45:48 -0400 | [diff] [blame] | 55 | var newPacket bool |
David Benjamin | 83c0bc9 | 2014-08-04 01:23:53 -0400 | [diff] [blame] | 56 | if len(b.data) == 0 { |
| 57 | // Pick some absurdly large buffer size. |
| 58 | b.resize(maxCiphertext + recordHeaderLen) |
| 59 | n, err := c.conn.Read(c.rawInput.data) |
| 60 | if err != nil { |
| 61 | return 0, nil, err |
| 62 | } |
David Benjamin | 13be1de | 2015-01-11 16:29:36 -0500 | [diff] [blame] | 63 | if c.config.Bugs.MaxPacketLength != 0 && n > c.config.Bugs.MaxPacketLength { |
| 64 | return 0, nil, fmt.Errorf("dtls: exceeded maximum packet length") |
| 65 | } |
David Benjamin | 83c0bc9 | 2014-08-04 01:23:53 -0400 | [diff] [blame] | 66 | c.rawInput.resize(n) |
David Benjamin | 30152fd | 2016-05-05 20:45:48 -0400 | [diff] [blame] | 67 | newPacket = true |
David Benjamin | 83c0bc9 | 2014-08-04 01:23:53 -0400 | [diff] [blame] | 68 | } |
| 69 | |
| 70 | // Read out one record. |
| 71 | // |
| 72 | // A real DTLS implementation should be tolerant of errors, |
| 73 | // but this is test code. We should not be tolerant of our |
| 74 | // peer sending garbage. |
| 75 | if len(b.data) < recordHeaderLen { |
| 76 | return 0, nil, errors.New("dtls: failed to read record header") |
| 77 | } |
| 78 | typ := recordType(b.data[0]) |
David Benjamin | b1dd8cd | 2016-09-26 19:20:48 -0400 | [diff] [blame] | 79 | vers := uint16(b.data[1])<<8 | uint16(b.data[2]) |
David Benjamin | bde0039 | 2016-06-21 12:19:28 -0400 | [diff] [blame] | 80 | // Alerts sent near version negotiation do not have a well-defined |
| 81 | // record-layer version prior to TLS 1.3. (In TLS 1.3, the record-layer |
| 82 | // version is irrelevant.) |
| 83 | if typ != recordTypeAlert { |
| 84 | if c.haveVers { |
Steven Valdez | c94998a | 2017-06-20 10:55:02 -0400 | [diff] [blame] | 85 | if vers != c.wireVersion { |
David Benjamin | bde0039 | 2016-06-21 12:19:28 -0400 | [diff] [blame] | 86 | c.sendAlert(alertProtocolVersion) |
Steven Valdez | c94998a | 2017-06-20 10:55:02 -0400 | [diff] [blame] | 87 | return 0, nil, c.in.setErrorLocked(fmt.Errorf("dtls: received record with version %x when expecting version %x", vers, c.wireVersion)) |
David Benjamin | bde0039 | 2016-06-21 12:19:28 -0400 | [diff] [blame] | 88 | } |
| 89 | } else { |
| 90 | // Pre-version-negotiation alerts may be sent with any version. |
| 91 | if expect := c.config.Bugs.ExpectInitialRecordVersion; expect != 0 && vers != expect { |
| 92 | c.sendAlert(alertProtocolVersion) |
| 93 | return 0, nil, c.in.setErrorLocked(fmt.Errorf("dtls: received record with version %x when expecting version %x", vers, expect)) |
| 94 | } |
David Benjamin | 1e29a6b | 2014-12-10 02:27:24 -0500 | [diff] [blame] | 95 | } |
David Benjamin | 83c0bc9 | 2014-08-04 01:23:53 -0400 | [diff] [blame] | 96 | } |
David Benjamin | 13e81fc | 2015-11-02 17:16:13 -0500 | [diff] [blame] | 97 | epoch := b.data[3:5] |
| 98 | seq := b.data[5:11] |
| 99 | // For test purposes, require the sequence number be monotonically |
| 100 | // increasing, so c.in includes the minimum next sequence number. Gaps |
| 101 | // may occur if packets failed to be sent out. A real implementation |
| 102 | // would maintain a replay window and such. |
| 103 | if !bytes.Equal(epoch, c.in.seq[:2]) { |
| 104 | c.sendAlert(alertIllegalParameter) |
| 105 | return 0, nil, c.in.setErrorLocked(fmt.Errorf("dtls: bad epoch")) |
| 106 | } |
| 107 | if bytes.Compare(seq, c.in.seq[2:]) < 0 { |
David Benjamin | 83c0bc9 | 2014-08-04 01:23:53 -0400 | [diff] [blame] | 108 | c.sendAlert(alertIllegalParameter) |
| 109 | return 0, nil, c.in.setErrorLocked(fmt.Errorf("dtls: bad sequence number")) |
| 110 | } |
David Benjamin | 13e81fc | 2015-11-02 17:16:13 -0500 | [diff] [blame] | 111 | copy(c.in.seq[2:], seq) |
David Benjamin | 83c0bc9 | 2014-08-04 01:23:53 -0400 | [diff] [blame] | 112 | n := int(b.data[11])<<8 | int(b.data[12]) |
| 113 | if n > maxCiphertext || len(b.data) < recordHeaderLen+n { |
| 114 | c.sendAlert(alertRecordOverflow) |
| 115 | return 0, nil, c.in.setErrorLocked(fmt.Errorf("dtls: oversized record received with length %d", n)) |
| 116 | } |
| 117 | |
| 118 | // Process message. |
| 119 | b, c.rawInput = c.in.splitBlock(b, recordHeaderLen+n) |
David Benjamin | ff26f09 | 2016-07-01 16:13:42 -0400 | [diff] [blame] | 120 | ok, off, _, alertValue := c.in.decrypt(b) |
David Benjamin | 83c0bc9 | 2014-08-04 01:23:53 -0400 | [diff] [blame] | 121 | if !ok { |
David Benjamin | ff26f09 | 2016-07-01 16:13:42 -0400 | [diff] [blame] | 122 | // A real DTLS implementation would silently ignore bad records, |
| 123 | // but we want to notice errors from the implementation under |
| 124 | // test. |
| 125 | return 0, nil, c.in.setErrorLocked(c.sendAlert(alertValue)) |
David Benjamin | 83c0bc9 | 2014-08-04 01:23:53 -0400 | [diff] [blame] | 126 | } |
| 127 | b.off = off |
David Benjamin | 30152fd | 2016-05-05 20:45:48 -0400 | [diff] [blame] | 128 | |
David Benjamin | ff26f09 | 2016-07-01 16:13:42 -0400 | [diff] [blame] | 129 | // TODO(nharper): Once DTLS 1.3 is defined, handle the extra |
| 130 | // parameter from decrypt. |
| 131 | |
David Benjamin | 30152fd | 2016-05-05 20:45:48 -0400 | [diff] [blame] | 132 | // Require that ChangeCipherSpec always share a packet with either the |
| 133 | // previous or next handshake message. |
| 134 | if newPacket && typ == recordTypeChangeCipherSpec && c.rawInput == nil { |
| 135 | return 0, nil, c.in.setErrorLocked(fmt.Errorf("dtls: ChangeCipherSpec not packed together with Finished")) |
| 136 | } |
| 137 | |
David Benjamin | 83c0bc9 | 2014-08-04 01:23:53 -0400 | [diff] [blame] | 138 | return typ, b, nil |
| 139 | } |
| 140 | |
David Benjamin | 7538122 | 2015-03-02 19:30:30 -0500 | [diff] [blame] | 141 | func (c *Conn) makeFragment(header, data []byte, fragOffset, fragLen int) []byte { |
| 142 | fragment := make([]byte, 0, 12+fragLen) |
| 143 | fragment = append(fragment, header...) |
| 144 | fragment = append(fragment, byte(c.sendHandshakeSeq>>8), byte(c.sendHandshakeSeq)) |
| 145 | fragment = append(fragment, byte(fragOffset>>16), byte(fragOffset>>8), byte(fragOffset)) |
| 146 | fragment = append(fragment, byte(fragLen>>16), byte(fragLen>>8), byte(fragLen)) |
| 147 | fragment = append(fragment, data[fragOffset:fragOffset+fragLen]...) |
| 148 | return fragment |
| 149 | } |
| 150 | |
David Benjamin | 83c0bc9 | 2014-08-04 01:23:53 -0400 | [diff] [blame] | 151 | func (c *Conn) dtlsWriteRecord(typ recordType, data []byte) (n int, err error) { |
David Benjamin | d660b57 | 2015-01-31 15:13:21 -0500 | [diff] [blame] | 152 | if typ != recordTypeHandshake { |
David Benjamin | b0c761e | 2017-06-25 22:42:55 -0400 | [diff] [blame^] | 153 | reorder := typ == recordTypeChangeCipherSpec && c.config.Bugs.ReorderChangeCipherSpec |
| 154 | |
| 155 | // Flush pending handshake messages before writing a new record. |
| 156 | if !reorder { |
| 157 | err = c.dtlsFlushHandshake() |
| 158 | if err != nil { |
| 159 | return |
| 160 | } |
| 161 | } |
| 162 | |
David Benjamin | d660b57 | 2015-01-31 15:13:21 -0500 | [diff] [blame] | 163 | // Only handshake messages are fragmented. |
David Benjamin | 8144f99 | 2016-06-22 17:05:13 -0400 | [diff] [blame] | 164 | n, err = c.dtlsWriteRawRecord(typ, data) |
| 165 | if err != nil { |
| 166 | return |
| 167 | } |
| 168 | |
David Benjamin | b0c761e | 2017-06-25 22:42:55 -0400 | [diff] [blame^] | 169 | if reorder { |
| 170 | err = c.dtlsFlushHandshake() |
| 171 | if err != nil { |
| 172 | return |
| 173 | } |
| 174 | } |
| 175 | |
David Benjamin | 8144f99 | 2016-06-22 17:05:13 -0400 | [diff] [blame] | 176 | if typ == recordTypeChangeCipherSpec { |
| 177 | err = c.out.changeCipherSpec(c.config) |
| 178 | if err != nil { |
David Benjamin | a818134 | 2017-07-07 18:10:57 -0400 | [diff] [blame] | 179 | return n, c.sendAlertLocked(alertLevelError, err.(alert)) |
David Benjamin | 8144f99 | 2016-06-22 17:05:13 -0400 | [diff] [blame] | 180 | } |
| 181 | } |
| 182 | return |
| 183 | } |
| 184 | |
| 185 | if c.out.cipher == nil && c.config.Bugs.StrayChangeCipherSpec { |
| 186 | _, err = c.dtlsWriteRawRecord(recordTypeChangeCipherSpec, []byte{1}) |
| 187 | if err != nil { |
| 188 | return |
| 189 | } |
David Benjamin | d660b57 | 2015-01-31 15:13:21 -0500 | [diff] [blame] | 190 | } |
| 191 | |
| 192 | maxLen := c.config.Bugs.MaxHandshakeRecordLength |
| 193 | if maxLen <= 0 { |
| 194 | maxLen = 1024 |
| 195 | } |
| 196 | |
| 197 | // Handshake messages have to be modified to include fragment |
| 198 | // offset and length and with the header replicated. Save the |
| 199 | // TLS header here. |
| 200 | // |
| 201 | // TODO(davidben): This assumes that data contains exactly one |
| 202 | // handshake message. This is incompatible with |
| 203 | // FragmentAcrossChangeCipherSpec. (Which is unfortunate |
| 204 | // because OpenSSL's DTLS implementation will probably accept |
| 205 | // such fragmentation and could do with a fix + tests.) |
David Benjamin | d660b57 | 2015-01-31 15:13:21 -0500 | [diff] [blame] | 206 | header := data[:4] |
| 207 | data = data[4:] |
| 208 | |
David Benjamin | a4e6d48 | 2015-03-02 19:10:53 -0500 | [diff] [blame] | 209 | isFinished := header[0] == typeFinished |
| 210 | |
David Benjamin | 7538122 | 2015-03-02 19:30:30 -0500 | [diff] [blame] | 211 | if c.config.Bugs.SendEmptyFragments { |
| 212 | fragment := c.makeFragment(header, data, 0, 0) |
| 213 | c.pendingFragments = append(c.pendingFragments, fragment) |
| 214 | } |
| 215 | |
David Benjamin | d660b57 | 2015-01-31 15:13:21 -0500 | [diff] [blame] | 216 | firstRun := true |
David Benjamin | 7538122 | 2015-03-02 19:30:30 -0500 | [diff] [blame] | 217 | fragOffset := 0 |
| 218 | for firstRun || fragOffset < len(data) { |
David Benjamin | d660b57 | 2015-01-31 15:13:21 -0500 | [diff] [blame] | 219 | firstRun = false |
David Benjamin | 7538122 | 2015-03-02 19:30:30 -0500 | [diff] [blame] | 220 | fragLen := len(data) - fragOffset |
| 221 | if fragLen > maxLen { |
| 222 | fragLen = maxLen |
David Benjamin | d660b57 | 2015-01-31 15:13:21 -0500 | [diff] [blame] | 223 | } |
| 224 | |
David Benjamin | 7538122 | 2015-03-02 19:30:30 -0500 | [diff] [blame] | 225 | fragment := c.makeFragment(header, data, fragOffset, fragLen) |
| 226 | if c.config.Bugs.FragmentMessageTypeMismatch && fragOffset > 0 { |
| 227 | fragment[0]++ |
| 228 | } |
| 229 | if c.config.Bugs.FragmentMessageLengthMismatch && fragOffset > 0 { |
| 230 | fragment[3]++ |
| 231 | } |
David Benjamin | d660b57 | 2015-01-31 15:13:21 -0500 | [diff] [blame] | 232 | |
David Benjamin | b3774b9 | 2015-01-31 17:16:01 -0500 | [diff] [blame] | 233 | // Buffer the fragment for later. They will be sent (and |
| 234 | // reordered) on flush. |
| 235 | c.pendingFragments = append(c.pendingFragments, fragment) |
David Benjamin | a4e6d48 | 2015-03-02 19:10:53 -0500 | [diff] [blame] | 236 | if c.config.Bugs.ReorderHandshakeFragments { |
| 237 | // Don't duplicate Finished to avoid the peer |
| 238 | // interpreting it as a retransmit request. |
| 239 | if !isFinished { |
| 240 | c.pendingFragments = append(c.pendingFragments, fragment) |
| 241 | } |
David Benjamin | b3774b9 | 2015-01-31 17:16:01 -0500 | [diff] [blame] | 242 | |
David Benjamin | 7538122 | 2015-03-02 19:30:30 -0500 | [diff] [blame] | 243 | if fragLen > (maxLen+1)/2 { |
David Benjamin | a4e6d48 | 2015-03-02 19:10:53 -0500 | [diff] [blame] | 244 | // Overlap each fragment by half. |
David Benjamin | 7538122 | 2015-03-02 19:30:30 -0500 | [diff] [blame] | 245 | fragLen = (maxLen + 1) / 2 |
David Benjamin | a4e6d48 | 2015-03-02 19:10:53 -0500 | [diff] [blame] | 246 | } |
David Benjamin | d660b57 | 2015-01-31 15:13:21 -0500 | [diff] [blame] | 247 | } |
David Benjamin | 7538122 | 2015-03-02 19:30:30 -0500 | [diff] [blame] | 248 | fragOffset += fragLen |
| 249 | n += fragLen |
| 250 | } |
| 251 | if !isFinished && c.config.Bugs.MixCompleteMessageWithFragments { |
| 252 | fragment := c.makeFragment(header, data, 0, len(data)) |
| 253 | c.pendingFragments = append(c.pendingFragments, fragment) |
David Benjamin | d660b57 | 2015-01-31 15:13:21 -0500 | [diff] [blame] | 254 | } |
| 255 | |
| 256 | // Increment the handshake sequence number for the next |
| 257 | // handshake message. |
| 258 | c.sendHandshakeSeq++ |
| 259 | return |
| 260 | } |
| 261 | |
David Benjamin | a4e6d48 | 2015-03-02 19:10:53 -0500 | [diff] [blame] | 262 | func (c *Conn) dtlsFlushHandshake() error { |
David Benjamin | bd15a8e | 2015-05-29 18:48:16 -0400 | [diff] [blame] | 263 | // This is a test-only DTLS implementation, so there is no need to |
| 264 | // retain |c.pendingFragments| for a future retransmit. |
David Benjamin | afbc63f | 2015-02-01 02:33:59 -0500 | [diff] [blame] | 265 | var fragments [][]byte |
David Benjamin | b3774b9 | 2015-01-31 17:16:01 -0500 | [diff] [blame] | 266 | fragments, c.pendingFragments = c.pendingFragments, fragments |
| 267 | |
| 268 | if c.config.Bugs.ReorderHandshakeFragments { |
David Benjamin | b3774b9 | 2015-01-31 17:16:01 -0500 | [diff] [blame] | 269 | perm := rand.New(rand.NewSource(0)).Perm(len(fragments)) |
| 270 | tmp := make([][]byte, len(fragments)) |
| 271 | for i := range tmp { |
| 272 | tmp[i] = fragments[perm[i]] |
| 273 | } |
| 274 | fragments = tmp |
David Benjamin | 6167281 | 2016-07-14 23:10:43 -0400 | [diff] [blame] | 275 | } else if c.config.Bugs.ReverseHandshakeFragments { |
| 276 | tmp := make([][]byte, len(fragments)) |
| 277 | for i := range tmp { |
| 278 | tmp[i] = fragments[len(fragments)-i-1] |
| 279 | } |
| 280 | fragments = tmp |
David Benjamin | b3774b9 | 2015-01-31 17:16:01 -0500 | [diff] [blame] | 281 | } |
| 282 | |
David Benjamin | bd15a8e | 2015-05-29 18:48:16 -0400 | [diff] [blame] | 283 | maxRecordLen := c.config.Bugs.PackHandshakeFragments |
| 284 | maxPacketLen := c.config.Bugs.PackHandshakeRecords |
| 285 | |
| 286 | // Pack handshake fragments into records. |
| 287 | var records [][]byte |
David Benjamin | b3774b9 | 2015-01-31 17:16:01 -0500 | [diff] [blame] | 288 | for _, fragment := range fragments { |
David Benjamin | 11fc66a | 2015-06-16 11:40:24 -0400 | [diff] [blame] | 289 | if n := c.config.Bugs.SplitFragments; n > 0 { |
| 290 | if len(fragment) > n { |
| 291 | records = append(records, fragment[:n]) |
| 292 | records = append(records, fragment[n:]) |
David Benjamin | bd15a8e | 2015-05-29 18:48:16 -0400 | [diff] [blame] | 293 | } else { |
| 294 | records = append(records, fragment) |
David Benjamin | 7538122 | 2015-03-02 19:30:30 -0500 | [diff] [blame] | 295 | } |
David Benjamin | bd15a8e | 2015-05-29 18:48:16 -0400 | [diff] [blame] | 296 | } else if i := len(records) - 1; len(records) > 0 && len(records[i])+len(fragment) <= maxRecordLen { |
| 297 | records[i] = append(records[i], fragment...) |
| 298 | } else { |
| 299 | // The fragment will be appended to, so copy it. |
| 300 | records = append(records, append([]byte{}, fragment...)) |
| 301 | } |
| 302 | } |
| 303 | |
| 304 | // Format them into packets. |
| 305 | var packets [][]byte |
| 306 | for _, record := range records { |
| 307 | b, err := c.dtlsSealRecord(recordTypeHandshake, record) |
| 308 | if err != nil { |
| 309 | return err |
David Benjamin | 7538122 | 2015-03-02 19:30:30 -0500 | [diff] [blame] | 310 | } |
| 311 | |
David Benjamin | bd15a8e | 2015-05-29 18:48:16 -0400 | [diff] [blame] | 312 | if i := len(packets) - 1; len(packets) > 0 && len(packets[i])+len(b.data) <= maxPacketLen { |
| 313 | packets[i] = append(packets[i], b.data...) |
| 314 | } else { |
| 315 | // The sealed record will be appended to and reused by |
| 316 | // |c.out|, so copy it. |
| 317 | packets = append(packets, append([]byte{}, b.data...)) |
| 318 | } |
| 319 | c.out.freeBlock(b) |
| 320 | } |
| 321 | |
| 322 | // Send all the packets. |
| 323 | for _, packet := range packets { |
| 324 | if _, err := c.conn.Write(packet); err != nil { |
David Benjamin | b3774b9 | 2015-01-31 17:16:01 -0500 | [diff] [blame] | 325 | return err |
| 326 | } |
| 327 | } |
| 328 | return nil |
| 329 | } |
| 330 | |
David Benjamin | bd15a8e | 2015-05-29 18:48:16 -0400 | [diff] [blame] | 331 | // dtlsSealRecord seals a record into a block from |c.out|'s pool. |
| 332 | func (c *Conn) dtlsSealRecord(typ recordType, data []byte) (b *block, err error) { |
David Benjamin | 83c0bc9 | 2014-08-04 01:23:53 -0400 | [diff] [blame] | 333 | recordHeaderLen := dtlsRecordHeaderLen |
| 334 | maxLen := c.config.Bugs.MaxHandshakeRecordLength |
| 335 | if maxLen <= 0 { |
| 336 | maxLen = 1024 |
| 337 | } |
| 338 | |
David Benjamin | bd15a8e | 2015-05-29 18:48:16 -0400 | [diff] [blame] | 339 | b = c.out.newBlock() |
David Benjamin | 83c0bc9 | 2014-08-04 01:23:53 -0400 | [diff] [blame] | 340 | |
David Benjamin | d660b57 | 2015-01-31 15:13:21 -0500 | [diff] [blame] | 341 | explicitIVLen := 0 |
| 342 | explicitIVIsSeq := false |
| 343 | |
| 344 | if cbc, ok := c.out.cipher.(cbcMode); ok { |
| 345 | // Block cipher modes have an explicit IV. |
| 346 | explicitIVLen = cbc.BlockSize() |
David Benjamin | e9a80ff | 2015-04-07 00:46:46 -0400 | [diff] [blame] | 347 | } else if aead, ok := c.out.cipher.(*tlsAead); ok { |
| 348 | if aead.explicitNonce { |
| 349 | explicitIVLen = 8 |
| 350 | // The AES-GCM construction in TLS has an explicit nonce so that |
| 351 | // the nonce can be random. However, the nonce is only 8 bytes |
| 352 | // which is too small for a secure, random nonce. Therefore we |
| 353 | // use the sequence number as the nonce. |
| 354 | explicitIVIsSeq = true |
| 355 | } |
David Benjamin | 7a4aaa4 | 2016-09-20 17:58:14 -0400 | [diff] [blame] | 356 | } else if _, ok := c.out.cipher.(nullCipher); !ok && c.out.cipher != nil { |
David Benjamin | d660b57 | 2015-01-31 15:13:21 -0500 | [diff] [blame] | 357 | panic("Unknown cipher") |
David Benjamin | 83c0bc9 | 2014-08-04 01:23:53 -0400 | [diff] [blame] | 358 | } |
David Benjamin | d660b57 | 2015-01-31 15:13:21 -0500 | [diff] [blame] | 359 | b.resize(recordHeaderLen + explicitIVLen + len(data)) |
Nick Harper | 1fd39d8 | 2016-06-14 18:14:35 -0700 | [diff] [blame] | 360 | // TODO(nharper): DTLS 1.3 will likely need to set this to |
| 361 | // recordTypeApplicationData if c.out.cipher != nil. |
David Benjamin | d660b57 | 2015-01-31 15:13:21 -0500 | [diff] [blame] | 362 | b.data[0] = byte(typ) |
Steven Valdez | c94998a | 2017-06-20 10:55:02 -0400 | [diff] [blame] | 363 | vers := c.wireVersion |
David Benjamin | d660b57 | 2015-01-31 15:13:21 -0500 | [diff] [blame] | 364 | if vers == 0 { |
| 365 | // Some TLS servers fail if the record version is greater than |
| 366 | // TLS 1.0 for the initial ClientHello. |
Steven Valdez | c94998a | 2017-06-20 10:55:02 -0400 | [diff] [blame] | 367 | if c.isDTLS { |
| 368 | vers = VersionDTLS10 |
| 369 | } else { |
| 370 | vers = VersionTLS10 |
| 371 | } |
David Benjamin | d660b57 | 2015-01-31 15:13:21 -0500 | [diff] [blame] | 372 | } |
David Benjamin | d660b57 | 2015-01-31 15:13:21 -0500 | [diff] [blame] | 373 | b.data[1] = byte(vers >> 8) |
| 374 | b.data[2] = byte(vers) |
| 375 | // DTLS records include an explicit sequence number. |
David Benjamin | 8e6db49 | 2015-07-25 18:29:23 -0400 | [diff] [blame] | 376 | copy(b.data[3:11], c.out.outSeq[0:]) |
David Benjamin | d660b57 | 2015-01-31 15:13:21 -0500 | [diff] [blame] | 377 | b.data[11] = byte(len(data) >> 8) |
| 378 | b.data[12] = byte(len(data)) |
| 379 | if explicitIVLen > 0 { |
| 380 | explicitIV := b.data[recordHeaderLen : recordHeaderLen+explicitIVLen] |
| 381 | if explicitIVIsSeq { |
David Benjamin | 8e6db49 | 2015-07-25 18:29:23 -0400 | [diff] [blame] | 382 | copy(explicitIV, c.out.outSeq[:]) |
David Benjamin | 83c0bc9 | 2014-08-04 01:23:53 -0400 | [diff] [blame] | 383 | } else { |
David Benjamin | d660b57 | 2015-01-31 15:13:21 -0500 | [diff] [blame] | 384 | if _, err = io.ReadFull(c.config.rand(), explicitIV); err != nil { |
| 385 | return |
David Benjamin | 83c0bc9 | 2014-08-04 01:23:53 -0400 | [diff] [blame] | 386 | } |
| 387 | } |
David Benjamin | 83c0bc9 | 2014-08-04 01:23:53 -0400 | [diff] [blame] | 388 | } |
David Benjamin | d660b57 | 2015-01-31 15:13:21 -0500 | [diff] [blame] | 389 | copy(b.data[recordHeaderLen+explicitIVLen:], data) |
Nick Harper | 1fd39d8 | 2016-06-14 18:14:35 -0700 | [diff] [blame] | 390 | c.out.encrypt(b, explicitIVLen, typ) |
David Benjamin | bd15a8e | 2015-05-29 18:48:16 -0400 | [diff] [blame] | 391 | return |
| 392 | } |
| 393 | |
| 394 | func (c *Conn) dtlsWriteRawRecord(typ recordType, data []byte) (n int, err error) { |
| 395 | b, err := c.dtlsSealRecord(typ, data) |
| 396 | if err != nil { |
| 397 | return |
| 398 | } |
David Benjamin | d660b57 | 2015-01-31 15:13:21 -0500 | [diff] [blame] | 399 | |
| 400 | _, err = c.conn.Write(b.data) |
| 401 | if err != nil { |
| 402 | return |
| 403 | } |
| 404 | n = len(data) |
| 405 | |
David Benjamin | 83c0bc9 | 2014-08-04 01:23:53 -0400 | [diff] [blame] | 406 | c.out.freeBlock(b) |
David Benjamin | 83c0bc9 | 2014-08-04 01:23:53 -0400 | [diff] [blame] | 407 | return |
| 408 | } |
| 409 | |
| 410 | func (c *Conn) dtlsDoReadHandshake() ([]byte, error) { |
| 411 | // Assemble a full handshake message. For test purposes, this |
David Benjamin | d9b091b | 2015-01-27 01:10:54 -0500 | [diff] [blame] | 412 | // implementation assumes fragments arrive in order. It may |
| 413 | // need to be cleverer if we ever test BoringSSL's retransmit |
| 414 | // behavior. |
David Benjamin | 83c0bc9 | 2014-08-04 01:23:53 -0400 | [diff] [blame] | 415 | for len(c.handMsg) < 4+c.handMsgLen { |
| 416 | // Get a new handshake record if the previous has been |
| 417 | // exhausted. |
| 418 | if c.hand.Len() == 0 { |
| 419 | if err := c.in.err; err != nil { |
| 420 | return nil, err |
| 421 | } |
| 422 | if err := c.readRecord(recordTypeHandshake); err != nil { |
| 423 | return nil, err |
| 424 | } |
| 425 | } |
| 426 | |
| 427 | // Read the next fragment. It must fit entirely within |
| 428 | // the record. |
| 429 | if c.hand.Len() < 12 { |
| 430 | return nil, errors.New("dtls: bad handshake record") |
| 431 | } |
| 432 | header := c.hand.Next(12) |
| 433 | fragN := int(header[1])<<16 | int(header[2])<<8 | int(header[3]) |
| 434 | fragSeq := uint16(header[4])<<8 | uint16(header[5]) |
| 435 | fragOff := int(header[6])<<16 | int(header[7])<<8 | int(header[8]) |
| 436 | fragLen := int(header[9])<<16 | int(header[10])<<8 | int(header[11]) |
| 437 | |
| 438 | if c.hand.Len() < fragLen { |
| 439 | return nil, errors.New("dtls: fragment length too long") |
| 440 | } |
| 441 | fragment := c.hand.Next(fragLen) |
| 442 | |
David Benjamin | d9b091b | 2015-01-27 01:10:54 -0500 | [diff] [blame] | 443 | // Check it's a fragment for the right message. |
| 444 | if fragSeq != c.recvHandshakeSeq { |
| 445 | return nil, errors.New("dtls: bad handshake sequence number") |
David Benjamin | 83c0bc9 | 2014-08-04 01:23:53 -0400 | [diff] [blame] | 446 | } |
| 447 | |
| 448 | // Check that the length is consistent. |
| 449 | if c.handMsg == nil { |
| 450 | c.handMsgLen = fragN |
| 451 | if c.handMsgLen > maxHandshake { |
| 452 | return nil, c.in.setErrorLocked(c.sendAlert(alertInternalError)) |
| 453 | } |
| 454 | // Start with the TLS handshake header, |
| 455 | // without the DTLS bits. |
| 456 | c.handMsg = append([]byte{}, header[:4]...) |
| 457 | } else if fragN != c.handMsgLen { |
| 458 | return nil, errors.New("dtls: bad handshake length") |
| 459 | } |
| 460 | |
| 461 | // Add the fragment to the pending message. |
| 462 | if 4+fragOff != len(c.handMsg) { |
| 463 | return nil, errors.New("dtls: bad fragment offset") |
| 464 | } |
| 465 | if fragOff+fragLen > c.handMsgLen { |
| 466 | return nil, errors.New("dtls: bad fragment length") |
| 467 | } |
| 468 | c.handMsg = append(c.handMsg, fragment...) |
| 469 | } |
| 470 | c.recvHandshakeSeq++ |
| 471 | ret := c.handMsg |
| 472 | c.handMsg, c.handMsgLen = nil, 0 |
| 473 | return ret, nil |
| 474 | } |
| 475 | |
| 476 | // DTLSServer returns a new DTLS server side connection |
| 477 | // using conn as the underlying transport. |
| 478 | // The configuration config must be non-nil and must have |
| 479 | // at least one certificate. |
| 480 | func DTLSServer(conn net.Conn, config *Config) *Conn { |
David Benjamin | 5e961c1 | 2014-11-07 01:48:35 -0500 | [diff] [blame] | 481 | c := &Conn{config: config, isDTLS: true, conn: conn} |
| 482 | c.init() |
| 483 | return c |
David Benjamin | 83c0bc9 | 2014-08-04 01:23:53 -0400 | [diff] [blame] | 484 | } |
| 485 | |
| 486 | // DTLSClient returns a new DTLS client side connection |
| 487 | // using conn as the underlying transport. |
| 488 | // The config cannot be nil: users must set either ServerHostname or |
| 489 | // InsecureSkipVerify in the config. |
| 490 | func DTLSClient(conn net.Conn, config *Config) *Conn { |
David Benjamin | 5e961c1 | 2014-11-07 01:48:35 -0500 | [diff] [blame] | 491 | c := &Conn{config: config, isClient: true, isDTLS: true, conn: conn} |
| 492 | c.init() |
| 493 | return c |
David Benjamin | 83c0bc9 | 2014-08-04 01:23:53 -0400 | [diff] [blame] | 494 | } |