]>
Commit | Line | Data |
---|---|---|
bae9f6d2 JC |
1 | // Copyright 2011 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 | package ssh | |
6 | ||
7 | import ( | |
8 | "crypto/aes" | |
9 | "crypto/cipher" | |
10 | "crypto/des" | |
11 | "crypto/rc4" | |
12 | "crypto/subtle" | |
13 | "encoding/binary" | |
14 | "errors" | |
15 | "fmt" | |
16 | "hash" | |
17 | "io" | |
18 | "io/ioutil" | |
19 | ) | |
20 | ||
21 | const ( | |
22 | packetSizeMultiple = 16 // TODO(huin) this should be determined by the cipher. | |
23 | ||
24 | // RFC 4253 section 6.1 defines a minimum packet size of 32768 that implementations | |
25 | // MUST be able to process (plus a few more kilobytes for padding and mac). The RFC | |
26 | // indicates implementations SHOULD be able to handle larger packet sizes, but then | |
27 | // waffles on about reasonable limits. | |
28 | // | |
29 | // OpenSSH caps their maxPacket at 256kB so we choose to do | |
30 | // the same. maxPacket is also used to ensure that uint32 | |
31 | // length fields do not overflow, so it should remain well | |
32 | // below 4G. | |
33 | maxPacket = 256 * 1024 | |
34 | ) | |
35 | ||
36 | // noneCipher implements cipher.Stream and provides no encryption. It is used | |
37 | // by the transport before the first key-exchange. | |
38 | type noneCipher struct{} | |
39 | ||
40 | func (c noneCipher) XORKeyStream(dst, src []byte) { | |
41 | copy(dst, src) | |
42 | } | |
43 | ||
44 | func newAESCTR(key, iv []byte) (cipher.Stream, error) { | |
45 | c, err := aes.NewCipher(key) | |
46 | if err != nil { | |
47 | return nil, err | |
48 | } | |
49 | return cipher.NewCTR(c, iv), nil | |
50 | } | |
51 | ||
52 | func newRC4(key, iv []byte) (cipher.Stream, error) { | |
53 | return rc4.NewCipher(key) | |
54 | } | |
55 | ||
56 | type streamCipherMode struct { | |
57 | keySize int | |
58 | ivSize int | |
59 | skip int | |
60 | createFunc func(key, iv []byte) (cipher.Stream, error) | |
61 | } | |
62 | ||
63 | func (c *streamCipherMode) createStream(key, iv []byte) (cipher.Stream, error) { | |
64 | if len(key) < c.keySize { | |
65 | panic("ssh: key length too small for cipher") | |
66 | } | |
67 | if len(iv) < c.ivSize { | |
68 | panic("ssh: iv too small for cipher") | |
69 | } | |
70 | ||
71 | stream, err := c.createFunc(key[:c.keySize], iv[:c.ivSize]) | |
72 | if err != nil { | |
73 | return nil, err | |
74 | } | |
75 | ||
76 | var streamDump []byte | |
77 | if c.skip > 0 { | |
78 | streamDump = make([]byte, 512) | |
79 | } | |
80 | ||
81 | for remainingToDump := c.skip; remainingToDump > 0; { | |
82 | dumpThisTime := remainingToDump | |
83 | if dumpThisTime > len(streamDump) { | |
84 | dumpThisTime = len(streamDump) | |
85 | } | |
86 | stream.XORKeyStream(streamDump[:dumpThisTime], streamDump[:dumpThisTime]) | |
87 | remainingToDump -= dumpThisTime | |
88 | } | |
89 | ||
90 | return stream, nil | |
91 | } | |
92 | ||
93 | // cipherModes documents properties of supported ciphers. Ciphers not included | |
94 | // are not supported and will not be negotiated, even if explicitly requested in | |
95 | // ClientConfig.Crypto.Ciphers. | |
96 | var cipherModes = map[string]*streamCipherMode{ | |
97 | // Ciphers from RFC4344, which introduced many CTR-based ciphers. Algorithms | |
98 | // are defined in the order specified in the RFC. | |
99 | "aes128-ctr": {16, aes.BlockSize, 0, newAESCTR}, | |
100 | "aes192-ctr": {24, aes.BlockSize, 0, newAESCTR}, | |
101 | "aes256-ctr": {32, aes.BlockSize, 0, newAESCTR}, | |
102 | ||
103 | // Ciphers from RFC4345, which introduces security-improved arcfour ciphers. | |
104 | // They are defined in the order specified in the RFC. | |
105 | "arcfour128": {16, 0, 1536, newRC4}, | |
106 | "arcfour256": {32, 0, 1536, newRC4}, | |
107 | ||
108 | // Cipher defined in RFC 4253, which describes SSH Transport Layer Protocol. | |
109 | // Note that this cipher is not safe, as stated in RFC 4253: "Arcfour (and | |
110 | // RC4) has problems with weak keys, and should be used with caution." | |
111 | // RFC4345 introduces improved versions of Arcfour. | |
112 | "arcfour": {16, 0, 0, newRC4}, | |
113 | ||
114 | // AES-GCM is not a stream cipher, so it is constructed with a | |
115 | // special case. If we add any more non-stream ciphers, we | |
116 | // should invest a cleaner way to do this. | |
117 | gcmCipherID: {16, 12, 0, nil}, | |
118 | ||
119 | // CBC mode is insecure and so is not included in the default config. | |
120 | // (See http://www.isg.rhul.ac.uk/~kp/SandPfinal.pdf). If absolutely | |
121 | // needed, it's possible to specify a custom Config to enable it. | |
122 | // You should expect that an active attacker can recover plaintext if | |
123 | // you do. | |
124 | aes128cbcID: {16, aes.BlockSize, 0, nil}, | |
125 | ||
126 | // 3des-cbc is insecure and is disabled by default. | |
127 | tripledescbcID: {24, des.BlockSize, 0, nil}, | |
128 | } | |
129 | ||
130 | // prefixLen is the length of the packet prefix that contains the packet length | |
131 | // and number of padding bytes. | |
132 | const prefixLen = 5 | |
133 | ||
134 | // streamPacketCipher is a packetCipher using a stream cipher. | |
135 | type streamPacketCipher struct { | |
136 | mac hash.Hash | |
137 | cipher cipher.Stream | |
138 | etm bool | |
139 | ||
140 | // The following members are to avoid per-packet allocations. | |
141 | prefix [prefixLen]byte | |
142 | seqNumBytes [4]byte | |
143 | padding [2 * packetSizeMultiple]byte | |
144 | packetData []byte | |
145 | macResult []byte | |
146 | } | |
147 | ||
148 | // readPacket reads and decrypt a single packet from the reader argument. | |
149 | func (s *streamPacketCipher) readPacket(seqNum uint32, r io.Reader) ([]byte, error) { | |
150 | if _, err := io.ReadFull(r, s.prefix[:]); err != nil { | |
151 | return nil, err | |
152 | } | |
153 | ||
154 | var encryptedPaddingLength [1]byte | |
155 | if s.mac != nil && s.etm { | |
156 | copy(encryptedPaddingLength[:], s.prefix[4:5]) | |
157 | s.cipher.XORKeyStream(s.prefix[4:5], s.prefix[4:5]) | |
158 | } else { | |
159 | s.cipher.XORKeyStream(s.prefix[:], s.prefix[:]) | |
160 | } | |
161 | ||
162 | length := binary.BigEndian.Uint32(s.prefix[0:4]) | |
163 | paddingLength := uint32(s.prefix[4]) | |
164 | ||
165 | var macSize uint32 | |
166 | if s.mac != nil { | |
167 | s.mac.Reset() | |
168 | binary.BigEndian.PutUint32(s.seqNumBytes[:], seqNum) | |
169 | s.mac.Write(s.seqNumBytes[:]) | |
170 | if s.etm { | |
171 | s.mac.Write(s.prefix[:4]) | |
172 | s.mac.Write(encryptedPaddingLength[:]) | |
173 | } else { | |
174 | s.mac.Write(s.prefix[:]) | |
175 | } | |
176 | macSize = uint32(s.mac.Size()) | |
177 | } | |
178 | ||
179 | if length <= paddingLength+1 { | |
180 | return nil, errors.New("ssh: invalid packet length, packet too small") | |
181 | } | |
182 | ||
183 | if length > maxPacket { | |
184 | return nil, errors.New("ssh: invalid packet length, packet too large") | |
185 | } | |
186 | ||
187 | // the maxPacket check above ensures that length-1+macSize | |
188 | // does not overflow. | |
189 | if uint32(cap(s.packetData)) < length-1+macSize { | |
190 | s.packetData = make([]byte, length-1+macSize) | |
191 | } else { | |
192 | s.packetData = s.packetData[:length-1+macSize] | |
193 | } | |
194 | ||
195 | if _, err := io.ReadFull(r, s.packetData); err != nil { | |
196 | return nil, err | |
197 | } | |
198 | mac := s.packetData[length-1:] | |
199 | data := s.packetData[:length-1] | |
200 | ||
201 | if s.mac != nil && s.etm { | |
202 | s.mac.Write(data) | |
203 | } | |
204 | ||
205 | s.cipher.XORKeyStream(data, data) | |
206 | ||
207 | if s.mac != nil { | |
208 | if !s.etm { | |
209 | s.mac.Write(data) | |
210 | } | |
211 | s.macResult = s.mac.Sum(s.macResult[:0]) | |
212 | if subtle.ConstantTimeCompare(s.macResult, mac) != 1 { | |
213 | return nil, errors.New("ssh: MAC failure") | |
214 | } | |
215 | } | |
216 | ||
217 | return s.packetData[:length-paddingLength-1], nil | |
218 | } | |
219 | ||
220 | // writePacket encrypts and sends a packet of data to the writer argument | |
221 | func (s *streamPacketCipher) writePacket(seqNum uint32, w io.Writer, rand io.Reader, packet []byte) error { | |
222 | if len(packet) > maxPacket { | |
223 | return errors.New("ssh: packet too large") | |
224 | } | |
225 | ||
226 | aadlen := 0 | |
227 | if s.mac != nil && s.etm { | |
228 | // packet length is not encrypted for EtM modes | |
229 | aadlen = 4 | |
230 | } | |
231 | ||
232 | paddingLength := packetSizeMultiple - (prefixLen+len(packet)-aadlen)%packetSizeMultiple | |
233 | if paddingLength < 4 { | |
234 | paddingLength += packetSizeMultiple | |
235 | } | |
236 | ||
237 | length := len(packet) + 1 + paddingLength | |
238 | binary.BigEndian.PutUint32(s.prefix[:], uint32(length)) | |
239 | s.prefix[4] = byte(paddingLength) | |
240 | padding := s.padding[:paddingLength] | |
241 | if _, err := io.ReadFull(rand, padding); err != nil { | |
242 | return err | |
243 | } | |
244 | ||
245 | if s.mac != nil { | |
246 | s.mac.Reset() | |
247 | binary.BigEndian.PutUint32(s.seqNumBytes[:], seqNum) | |
248 | s.mac.Write(s.seqNumBytes[:]) | |
249 | ||
250 | if s.etm { | |
251 | // For EtM algorithms, the packet length must stay unencrypted, | |
252 | // but the following data (padding length) must be encrypted | |
253 | s.cipher.XORKeyStream(s.prefix[4:5], s.prefix[4:5]) | |
254 | } | |
255 | ||
256 | s.mac.Write(s.prefix[:]) | |
257 | ||
258 | if !s.etm { | |
259 | // For non-EtM algorithms, the algorithm is applied on unencrypted data | |
260 | s.mac.Write(packet) | |
261 | s.mac.Write(padding) | |
262 | } | |
263 | } | |
264 | ||
265 | if !(s.mac != nil && s.etm) { | |
266 | // For EtM algorithms, the padding length has already been encrypted | |
267 | // and the packet length must remain unencrypted | |
268 | s.cipher.XORKeyStream(s.prefix[:], s.prefix[:]) | |
269 | } | |
270 | ||
271 | s.cipher.XORKeyStream(packet, packet) | |
272 | s.cipher.XORKeyStream(padding, padding) | |
273 | ||
274 | if s.mac != nil && s.etm { | |
275 | // For EtM algorithms, packet and padding must be encrypted | |
276 | s.mac.Write(packet) | |
277 | s.mac.Write(padding) | |
278 | } | |
279 | ||
280 | if _, err := w.Write(s.prefix[:]); err != nil { | |
281 | return err | |
282 | } | |
283 | if _, err := w.Write(packet); err != nil { | |
284 | return err | |
285 | } | |
286 | if _, err := w.Write(padding); err != nil { | |
287 | return err | |
288 | } | |
289 | ||
290 | if s.mac != nil { | |
291 | s.macResult = s.mac.Sum(s.macResult[:0]) | |
292 | if _, err := w.Write(s.macResult); err != nil { | |
293 | return err | |
294 | } | |
295 | } | |
296 | ||
297 | return nil | |
298 | } | |
299 | ||
300 | type gcmCipher struct { | |
301 | aead cipher.AEAD | |
302 | prefix [4]byte | |
303 | iv []byte | |
304 | buf []byte | |
305 | } | |
306 | ||
307 | func newGCMCipher(iv, key, macKey []byte) (packetCipher, error) { | |
308 | c, err := aes.NewCipher(key) | |
309 | if err != nil { | |
310 | return nil, err | |
311 | } | |
312 | ||
313 | aead, err := cipher.NewGCM(c) | |
314 | if err != nil { | |
315 | return nil, err | |
316 | } | |
317 | ||
318 | return &gcmCipher{ | |
319 | aead: aead, | |
320 | iv: iv, | |
321 | }, nil | |
322 | } | |
323 | ||
324 | const gcmTagSize = 16 | |
325 | ||
326 | func (c *gcmCipher) writePacket(seqNum uint32, w io.Writer, rand io.Reader, packet []byte) error { | |
327 | // Pad out to multiple of 16 bytes. This is different from the | |
328 | // stream cipher because that encrypts the length too. | |
329 | padding := byte(packetSizeMultiple - (1+len(packet))%packetSizeMultiple) | |
330 | if padding < 4 { | |
331 | padding += packetSizeMultiple | |
332 | } | |
333 | ||
334 | length := uint32(len(packet) + int(padding) + 1) | |
335 | binary.BigEndian.PutUint32(c.prefix[:], length) | |
336 | if _, err := w.Write(c.prefix[:]); err != nil { | |
337 | return err | |
338 | } | |
339 | ||
340 | if cap(c.buf) < int(length) { | |
341 | c.buf = make([]byte, length) | |
342 | } else { | |
343 | c.buf = c.buf[:length] | |
344 | } | |
345 | ||
346 | c.buf[0] = padding | |
347 | copy(c.buf[1:], packet) | |
348 | if _, err := io.ReadFull(rand, c.buf[1+len(packet):]); err != nil { | |
349 | return err | |
350 | } | |
351 | c.buf = c.aead.Seal(c.buf[:0], c.iv, c.buf, c.prefix[:]) | |
352 | if _, err := w.Write(c.buf); err != nil { | |
353 | return err | |
354 | } | |
355 | c.incIV() | |
356 | ||
357 | return nil | |
358 | } | |
359 | ||
360 | func (c *gcmCipher) incIV() { | |
361 | for i := 4 + 7; i >= 4; i-- { | |
362 | c.iv[i]++ | |
363 | if c.iv[i] != 0 { | |
364 | break | |
365 | } | |
366 | } | |
367 | } | |
368 | ||
369 | func (c *gcmCipher) readPacket(seqNum uint32, r io.Reader) ([]byte, error) { | |
370 | if _, err := io.ReadFull(r, c.prefix[:]); err != nil { | |
371 | return nil, err | |
372 | } | |
373 | length := binary.BigEndian.Uint32(c.prefix[:]) | |
374 | if length > maxPacket { | |
375 | return nil, errors.New("ssh: max packet length exceeded.") | |
376 | } | |
377 | ||
378 | if cap(c.buf) < int(length+gcmTagSize) { | |
379 | c.buf = make([]byte, length+gcmTagSize) | |
380 | } else { | |
381 | c.buf = c.buf[:length+gcmTagSize] | |
382 | } | |
383 | ||
384 | if _, err := io.ReadFull(r, c.buf); err != nil { | |
385 | return nil, err | |
386 | } | |
387 | ||
388 | plain, err := c.aead.Open(c.buf[:0], c.iv, c.buf, c.prefix[:]) | |
389 | if err != nil { | |
390 | return nil, err | |
391 | } | |
392 | c.incIV() | |
393 | ||
394 | padding := plain[0] | |
395 | if padding < 4 || padding >= 20 { | |
396 | return nil, fmt.Errorf("ssh: illegal padding %d", padding) | |
397 | } | |
398 | ||
399 | if int(padding+1) >= len(plain) { | |
400 | return nil, fmt.Errorf("ssh: padding %d too large", padding) | |
401 | } | |
402 | plain = plain[1 : length-uint32(padding)] | |
403 | return plain, nil | |
404 | } | |
405 | ||
406 | // cbcCipher implements aes128-cbc cipher defined in RFC 4253 section 6.1 | |
407 | type cbcCipher struct { | |
408 | mac hash.Hash | |
409 | macSize uint32 | |
410 | decrypter cipher.BlockMode | |
411 | encrypter cipher.BlockMode | |
412 | ||
413 | // The following members are to avoid per-packet allocations. | |
414 | seqNumBytes [4]byte | |
415 | packetData []byte | |
416 | macResult []byte | |
417 | ||
418 | // Amount of data we should still read to hide which | |
419 | // verification error triggered. | |
420 | oracleCamouflage uint32 | |
421 | } | |
422 | ||
423 | func newCBCCipher(c cipher.Block, iv, key, macKey []byte, algs directionAlgorithms) (packetCipher, error) { | |
424 | cbc := &cbcCipher{ | |
425 | mac: macModes[algs.MAC].new(macKey), | |
426 | decrypter: cipher.NewCBCDecrypter(c, iv), | |
427 | encrypter: cipher.NewCBCEncrypter(c, iv), | |
428 | packetData: make([]byte, 1024), | |
429 | } | |
430 | if cbc.mac != nil { | |
431 | cbc.macSize = uint32(cbc.mac.Size()) | |
432 | } | |
433 | ||
434 | return cbc, nil | |
435 | } | |
436 | ||
437 | func newAESCBCCipher(iv, key, macKey []byte, algs directionAlgorithms) (packetCipher, error) { | |
438 | c, err := aes.NewCipher(key) | |
439 | if err != nil { | |
440 | return nil, err | |
441 | } | |
442 | ||
443 | cbc, err := newCBCCipher(c, iv, key, macKey, algs) | |
444 | if err != nil { | |
445 | return nil, err | |
446 | } | |
447 | ||
448 | return cbc, nil | |
449 | } | |
450 | ||
451 | func newTripleDESCBCCipher(iv, key, macKey []byte, algs directionAlgorithms) (packetCipher, error) { | |
452 | c, err := des.NewTripleDESCipher(key) | |
453 | if err != nil { | |
454 | return nil, err | |
455 | } | |
456 | ||
457 | cbc, err := newCBCCipher(c, iv, key, macKey, algs) | |
458 | if err != nil { | |
459 | return nil, err | |
460 | } | |
461 | ||
462 | return cbc, nil | |
463 | } | |
464 | ||
465 | func maxUInt32(a, b int) uint32 { | |
466 | if a > b { | |
467 | return uint32(a) | |
468 | } | |
469 | return uint32(b) | |
470 | } | |
471 | ||
472 | const ( | |
473 | cbcMinPacketSizeMultiple = 8 | |
474 | cbcMinPacketSize = 16 | |
475 | cbcMinPaddingSize = 4 | |
476 | ) | |
477 | ||
478 | // cbcError represents a verification error that may leak information. | |
479 | type cbcError string | |
480 | ||
481 | func (e cbcError) Error() string { return string(e) } | |
482 | ||
483 | func (c *cbcCipher) readPacket(seqNum uint32, r io.Reader) ([]byte, error) { | |
484 | p, err := c.readPacketLeaky(seqNum, r) | |
485 | if err != nil { | |
486 | if _, ok := err.(cbcError); ok { | |
487 | // Verification error: read a fixed amount of | |
488 | // data, to make distinguishing between | |
489 | // failing MAC and failing length check more | |
490 | // difficult. | |
491 | io.CopyN(ioutil.Discard, r, int64(c.oracleCamouflage)) | |
492 | } | |
493 | } | |
494 | return p, err | |
495 | } | |
496 | ||
497 | func (c *cbcCipher) readPacketLeaky(seqNum uint32, r io.Reader) ([]byte, error) { | |
498 | blockSize := c.decrypter.BlockSize() | |
499 | ||
500 | // Read the header, which will include some of the subsequent data in the | |
501 | // case of block ciphers - this is copied back to the payload later. | |
502 | // How many bytes of payload/padding will be read with this first read. | |
503 | firstBlockLength := uint32((prefixLen + blockSize - 1) / blockSize * blockSize) | |
504 | firstBlock := c.packetData[:firstBlockLength] | |
505 | if _, err := io.ReadFull(r, firstBlock); err != nil { | |
506 | return nil, err | |
507 | } | |
508 | ||
509 | c.oracleCamouflage = maxPacket + 4 + c.macSize - firstBlockLength | |
510 | ||
511 | c.decrypter.CryptBlocks(firstBlock, firstBlock) | |
512 | length := binary.BigEndian.Uint32(firstBlock[:4]) | |
513 | if length > maxPacket { | |
514 | return nil, cbcError("ssh: packet too large") | |
515 | } | |
516 | if length+4 < maxUInt32(cbcMinPacketSize, blockSize) { | |
517 | // The minimum size of a packet is 16 (or the cipher block size, whichever | |
518 | // is larger) bytes. | |
519 | return nil, cbcError("ssh: packet too small") | |
520 | } | |
521 | // The length of the packet (including the length field but not the MAC) must | |
522 | // be a multiple of the block size or 8, whichever is larger. | |
523 | if (length+4)%maxUInt32(cbcMinPacketSizeMultiple, blockSize) != 0 { | |
524 | return nil, cbcError("ssh: invalid packet length multiple") | |
525 | } | |
526 | ||
527 | paddingLength := uint32(firstBlock[4]) | |
528 | if paddingLength < cbcMinPaddingSize || length <= paddingLength+1 { | |
529 | return nil, cbcError("ssh: invalid packet length") | |
530 | } | |
531 | ||
532 | // Positions within the c.packetData buffer: | |
533 | macStart := 4 + length | |
534 | paddingStart := macStart - paddingLength | |
535 | ||
536 | // Entire packet size, starting before length, ending at end of mac. | |
537 | entirePacketSize := macStart + c.macSize | |
538 | ||
539 | // Ensure c.packetData is large enough for the entire packet data. | |
540 | if uint32(cap(c.packetData)) < entirePacketSize { | |
541 | // Still need to upsize and copy, but this should be rare at runtime, only | |
542 | // on upsizing the packetData buffer. | |
543 | c.packetData = make([]byte, entirePacketSize) | |
544 | copy(c.packetData, firstBlock) | |
545 | } else { | |
546 | c.packetData = c.packetData[:entirePacketSize] | |
547 | } | |
548 | ||
549 | if n, err := io.ReadFull(r, c.packetData[firstBlockLength:]); err != nil { | |
550 | return nil, err | |
551 | } else { | |
552 | c.oracleCamouflage -= uint32(n) | |
553 | } | |
554 | ||
555 | remainingCrypted := c.packetData[firstBlockLength:macStart] | |
556 | c.decrypter.CryptBlocks(remainingCrypted, remainingCrypted) | |
557 | ||
558 | mac := c.packetData[macStart:] | |
559 | if c.mac != nil { | |
560 | c.mac.Reset() | |
561 | binary.BigEndian.PutUint32(c.seqNumBytes[:], seqNum) | |
562 | c.mac.Write(c.seqNumBytes[:]) | |
563 | c.mac.Write(c.packetData[:macStart]) | |
564 | c.macResult = c.mac.Sum(c.macResult[:0]) | |
565 | if subtle.ConstantTimeCompare(c.macResult, mac) != 1 { | |
566 | return nil, cbcError("ssh: MAC failure") | |
567 | } | |
568 | } | |
569 | ||
570 | return c.packetData[prefixLen:paddingStart], nil | |
571 | } | |
572 | ||
573 | func (c *cbcCipher) writePacket(seqNum uint32, w io.Writer, rand io.Reader, packet []byte) error { | |
574 | effectiveBlockSize := maxUInt32(cbcMinPacketSizeMultiple, c.encrypter.BlockSize()) | |
575 | ||
576 | // Length of encrypted portion of the packet (header, payload, padding). | |
577 | // Enforce minimum padding and packet size. | |
578 | encLength := maxUInt32(prefixLen+len(packet)+cbcMinPaddingSize, cbcMinPaddingSize) | |
579 | // Enforce block size. | |
580 | encLength = (encLength + effectiveBlockSize - 1) / effectiveBlockSize * effectiveBlockSize | |
581 | ||
582 | length := encLength - 4 | |
583 | paddingLength := int(length) - (1 + len(packet)) | |
584 | ||
585 | // Overall buffer contains: header, payload, padding, mac. | |
586 | // Space for the MAC is reserved in the capacity but not the slice length. | |
587 | bufferSize := encLength + c.macSize | |
588 | if uint32(cap(c.packetData)) < bufferSize { | |
589 | c.packetData = make([]byte, encLength, bufferSize) | |
590 | } else { | |
591 | c.packetData = c.packetData[:encLength] | |
592 | } | |
593 | ||
594 | p := c.packetData | |
595 | ||
596 | // Packet header. | |
597 | binary.BigEndian.PutUint32(p, length) | |
598 | p = p[4:] | |
599 | p[0] = byte(paddingLength) | |
600 | ||
601 | // Payload. | |
602 | p = p[1:] | |
603 | copy(p, packet) | |
604 | ||
605 | // Padding. | |
606 | p = p[len(packet):] | |
607 | if _, err := io.ReadFull(rand, p); err != nil { | |
608 | return err | |
609 | } | |
610 | ||
611 | if c.mac != nil { | |
612 | c.mac.Reset() | |
613 | binary.BigEndian.PutUint32(c.seqNumBytes[:], seqNum) | |
614 | c.mac.Write(c.seqNumBytes[:]) | |
615 | c.mac.Write(c.packetData) | |
616 | // The MAC is now appended into the capacity reserved for it earlier. | |
617 | c.packetData = c.mac.Sum(c.packetData) | |
618 | } | |
619 | ||
620 | c.encrypter.CryptBlocks(c.packetData[:encLength], c.packetData[:encLength]) | |
621 | ||
622 | if _, err := w.Write(c.packetData); err != nil { | |
623 | return err | |
624 | } | |
625 | ||
626 | return nil | |
627 | } |