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.
21 "golang.org/x/crypto/internal/chacha20"
22 "golang.org/x/crypto/poly1305"
26 packetSizeMultiple = 16 // TODO(huin) this should be determined by the cipher.
28 // RFC 4253 section 6.1 defines a minimum packet size of 32768 that implementations
29 // MUST be able to process (plus a few more kilobytes for padding and mac). The RFC
30 // indicates implementations SHOULD be able to handle larger packet sizes, but then
31 // waffles on about reasonable limits.
33 // OpenSSH caps their maxPacket at 256kB so we choose to do
34 // the same. maxPacket is also used to ensure that uint32
35 // length fields do not overflow, so it should remain well
37 maxPacket = 256 * 1024
40 // noneCipher implements cipher.Stream and provides no encryption. It is used
41 // by the transport before the first key-exchange.
42 type noneCipher struct{}
44 func (c noneCipher) XORKeyStream(dst, src []byte) {
48 func newAESCTR(key, iv []byte) (cipher.Stream, error) {
49 c, err := aes.NewCipher(key)
53 return cipher.NewCTR(c, iv), nil
56 func newRC4(key, iv []byte) (cipher.Stream, error) {
57 return rc4.NewCipher(key)
60 type cipherMode struct {
63 create func(key, iv []byte, macKey []byte, algs directionAlgorithms) (packetCipher, error)
66 func streamCipherMode(skip int, createFunc func(key, iv []byte) (cipher.Stream, error)) func(key, iv []byte, macKey []byte, algs directionAlgorithms) (packetCipher, error) {
67 return func(key, iv, macKey []byte, algs directionAlgorithms) (packetCipher, error) {
68 stream, err := createFunc(key, iv)
75 streamDump = make([]byte, 512)
78 for remainingToDump := skip; remainingToDump > 0; {
79 dumpThisTime := remainingToDump
80 if dumpThisTime > len(streamDump) {
81 dumpThisTime = len(streamDump)
83 stream.XORKeyStream(streamDump[:dumpThisTime], streamDump[:dumpThisTime])
84 remainingToDump -= dumpThisTime
87 mac := macModes[algs.MAC].new(macKey)
88 return &streamPacketCipher{
90 etm: macModes[algs.MAC].etm,
91 macResult: make([]byte, mac.Size()),
97 // cipherModes documents properties of supported ciphers. Ciphers not included
98 // are not supported and will not be negotiated, even if explicitly requested in
99 // ClientConfig.Crypto.Ciphers.
100 var cipherModes = map[string]*cipherMode{
101 // Ciphers from RFC4344, which introduced many CTR-based ciphers. Algorithms
102 // are defined in the order specified in the RFC.
103 "aes128-ctr": {16, aes.BlockSize, streamCipherMode(0, newAESCTR)},
104 "aes192-ctr": {24, aes.BlockSize, streamCipherMode(0, newAESCTR)},
105 "aes256-ctr": {32, aes.BlockSize, streamCipherMode(0, newAESCTR)},
107 // Ciphers from RFC4345, which introduces security-improved arcfour ciphers.
108 // They are defined in the order specified in the RFC.
109 "arcfour128": {16, 0, streamCipherMode(1536, newRC4)},
110 "arcfour256": {32, 0, streamCipherMode(1536, newRC4)},
112 // Cipher defined in RFC 4253, which describes SSH Transport Layer Protocol.
113 // Note that this cipher is not safe, as stated in RFC 4253: "Arcfour (and
114 // RC4) has problems with weak keys, and should be used with caution."
115 // RFC4345 introduces improved versions of Arcfour.
116 "arcfour": {16, 0, streamCipherMode(0, newRC4)},
119 gcmCipherID: {16, 12, newGCMCipher},
120 chacha20Poly1305ID: {64, 0, newChaCha20Cipher},
122 // CBC mode is insecure and so is not included in the default config.
123 // (See http://www.isg.rhul.ac.uk/~kp/SandPfinal.pdf). If absolutely
124 // needed, it's possible to specify a custom Config to enable it.
125 // You should expect that an active attacker can recover plaintext if
127 aes128cbcID: {16, aes.BlockSize, newAESCBCCipher},
129 // 3des-cbc is insecure and is not included in the default
131 tripledescbcID: {24, des.BlockSize, newTripleDESCBCCipher},
134 // prefixLen is the length of the packet prefix that contains the packet length
135 // and number of padding bytes.
138 // streamPacketCipher is a packetCipher using a stream cipher.
139 type streamPacketCipher struct {
144 // The following members are to avoid per-packet allocations.
145 prefix [prefixLen]byte
147 padding [2 * packetSizeMultiple]byte
152 // readPacket reads and decrypt a single packet from the reader argument.
153 func (s *streamPacketCipher) readPacket(seqNum uint32, r io.Reader) ([]byte, error) {
154 if _, err := io.ReadFull(r, s.prefix[:]); err != nil {
158 var encryptedPaddingLength [1]byte
159 if s.mac != nil && s.etm {
160 copy(encryptedPaddingLength[:], s.prefix[4:5])
161 s.cipher.XORKeyStream(s.prefix[4:5], s.prefix[4:5])
163 s.cipher.XORKeyStream(s.prefix[:], s.prefix[:])
166 length := binary.BigEndian.Uint32(s.prefix[0:4])
167 paddingLength := uint32(s.prefix[4])
172 binary.BigEndian.PutUint32(s.seqNumBytes[:], seqNum)
173 s.mac.Write(s.seqNumBytes[:])
175 s.mac.Write(s.prefix[:4])
176 s.mac.Write(encryptedPaddingLength[:])
178 s.mac.Write(s.prefix[:])
180 macSize = uint32(s.mac.Size())
183 if length <= paddingLength+1 {
184 return nil, errors.New("ssh: invalid packet length, packet too small")
187 if length > maxPacket {
188 return nil, errors.New("ssh: invalid packet length, packet too large")
191 // the maxPacket check above ensures that length-1+macSize
192 // does not overflow.
193 if uint32(cap(s.packetData)) < length-1+macSize {
194 s.packetData = make([]byte, length-1+macSize)
196 s.packetData = s.packetData[:length-1+macSize]
199 if _, err := io.ReadFull(r, s.packetData); err != nil {
202 mac := s.packetData[length-1:]
203 data := s.packetData[:length-1]
205 if s.mac != nil && s.etm {
209 s.cipher.XORKeyStream(data, data)
215 s.macResult = s.mac.Sum(s.macResult[:0])
216 if subtle.ConstantTimeCompare(s.macResult, mac) != 1 {
217 return nil, errors.New("ssh: MAC failure")
221 return s.packetData[:length-paddingLength-1], nil
224 // writePacket encrypts and sends a packet of data to the writer argument
225 func (s *streamPacketCipher) writePacket(seqNum uint32, w io.Writer, rand io.Reader, packet []byte) error {
226 if len(packet) > maxPacket {
227 return errors.New("ssh: packet too large")
231 if s.mac != nil && s.etm {
232 // packet length is not encrypted for EtM modes
236 paddingLength := packetSizeMultiple - (prefixLen+len(packet)-aadlen)%packetSizeMultiple
237 if paddingLength < 4 {
238 paddingLength += packetSizeMultiple
241 length := len(packet) + 1 + paddingLength
242 binary.BigEndian.PutUint32(s.prefix[:], uint32(length))
243 s.prefix[4] = byte(paddingLength)
244 padding := s.padding[:paddingLength]
245 if _, err := io.ReadFull(rand, padding); err != nil {
251 binary.BigEndian.PutUint32(s.seqNumBytes[:], seqNum)
252 s.mac.Write(s.seqNumBytes[:])
255 // For EtM algorithms, the packet length must stay unencrypted,
256 // but the following data (padding length) must be encrypted
257 s.cipher.XORKeyStream(s.prefix[4:5], s.prefix[4:5])
260 s.mac.Write(s.prefix[:])
263 // For non-EtM algorithms, the algorithm is applied on unencrypted data
269 if !(s.mac != nil && s.etm) {
270 // For EtM algorithms, the padding length has already been encrypted
271 // and the packet length must remain unencrypted
272 s.cipher.XORKeyStream(s.prefix[:], s.prefix[:])
275 s.cipher.XORKeyStream(packet, packet)
276 s.cipher.XORKeyStream(padding, padding)
278 if s.mac != nil && s.etm {
279 // For EtM algorithms, packet and padding must be encrypted
284 if _, err := w.Write(s.prefix[:]); err != nil {
287 if _, err := w.Write(packet); err != nil {
290 if _, err := w.Write(padding); err != nil {
295 s.macResult = s.mac.Sum(s.macResult[:0])
296 if _, err := w.Write(s.macResult); err != nil {
304 type gcmCipher struct {
311 func newGCMCipher(key, iv, unusedMacKey []byte, unusedAlgs directionAlgorithms) (packetCipher, error) {
312 c, err := aes.NewCipher(key)
317 aead, err := cipher.NewGCM(c)
328 const gcmTagSize = 16
330 func (c *gcmCipher) writePacket(seqNum uint32, w io.Writer, rand io.Reader, packet []byte) error {
331 // Pad out to multiple of 16 bytes. This is different from the
332 // stream cipher because that encrypts the length too.
333 padding := byte(packetSizeMultiple - (1+len(packet))%packetSizeMultiple)
335 padding += packetSizeMultiple
338 length := uint32(len(packet) + int(padding) + 1)
339 binary.BigEndian.PutUint32(c.prefix[:], length)
340 if _, err := w.Write(c.prefix[:]); err != nil {
344 if cap(c.buf) < int(length) {
345 c.buf = make([]byte, length)
347 c.buf = c.buf[:length]
351 copy(c.buf[1:], packet)
352 if _, err := io.ReadFull(rand, c.buf[1+len(packet):]); err != nil {
355 c.buf = c.aead.Seal(c.buf[:0], c.iv, c.buf, c.prefix[:])
356 if _, err := w.Write(c.buf); err != nil {
364 func (c *gcmCipher) incIV() {
365 for i := 4 + 7; i >= 4; i-- {
373 func (c *gcmCipher) readPacket(seqNum uint32, r io.Reader) ([]byte, error) {
374 if _, err := io.ReadFull(r, c.prefix[:]); err != nil {
377 length := binary.BigEndian.Uint32(c.prefix[:])
378 if length > maxPacket {
379 return nil, errors.New("ssh: max packet length exceeded")
382 if cap(c.buf) < int(length+gcmTagSize) {
383 c.buf = make([]byte, length+gcmTagSize)
385 c.buf = c.buf[:length+gcmTagSize]
388 if _, err := io.ReadFull(r, c.buf); err != nil {
392 plain, err := c.aead.Open(c.buf[:0], c.iv, c.buf, c.prefix[:])
400 // padding is a byte, so it automatically satisfies
401 // the maximum size, which is 255.
402 return nil, fmt.Errorf("ssh: illegal padding %d", padding)
405 if int(padding+1) >= len(plain) {
406 return nil, fmt.Errorf("ssh: padding %d too large", padding)
408 plain = plain[1 : length-uint32(padding)]
412 // cbcCipher implements aes128-cbc cipher defined in RFC 4253 section 6.1
413 type cbcCipher struct {
416 decrypter cipher.BlockMode
417 encrypter cipher.BlockMode
419 // The following members are to avoid per-packet allocations.
424 // Amount of data we should still read to hide which
425 // verification error triggered.
426 oracleCamouflage uint32
429 func newCBCCipher(c cipher.Block, key, iv, macKey []byte, algs directionAlgorithms) (packetCipher, error) {
431 mac: macModes[algs.MAC].new(macKey),
432 decrypter: cipher.NewCBCDecrypter(c, iv),
433 encrypter: cipher.NewCBCEncrypter(c, iv),
434 packetData: make([]byte, 1024),
437 cbc.macSize = uint32(cbc.mac.Size())
443 func newAESCBCCipher(key, iv, macKey []byte, algs directionAlgorithms) (packetCipher, error) {
444 c, err := aes.NewCipher(key)
449 cbc, err := newCBCCipher(c, key, iv, macKey, algs)
457 func newTripleDESCBCCipher(key, iv, macKey []byte, algs directionAlgorithms) (packetCipher, error) {
458 c, err := des.NewTripleDESCipher(key)
463 cbc, err := newCBCCipher(c, key, iv, macKey, algs)
471 func maxUInt32(a, b int) uint32 {
479 cbcMinPacketSizeMultiple = 8
480 cbcMinPacketSize = 16
481 cbcMinPaddingSize = 4
484 // cbcError represents a verification error that may leak information.
487 func (e cbcError) Error() string { return string(e) }
489 func (c *cbcCipher) readPacket(seqNum uint32, r io.Reader) ([]byte, error) {
490 p, err := c.readPacketLeaky(seqNum, r)
492 if _, ok := err.(cbcError); ok {
493 // Verification error: read a fixed amount of
494 // data, to make distinguishing between
495 // failing MAC and failing length check more
497 io.CopyN(ioutil.Discard, r, int64(c.oracleCamouflage))
503 func (c *cbcCipher) readPacketLeaky(seqNum uint32, r io.Reader) ([]byte, error) {
504 blockSize := c.decrypter.BlockSize()
506 // Read the header, which will include some of the subsequent data in the
507 // case of block ciphers - this is copied back to the payload later.
508 // How many bytes of payload/padding will be read with this first read.
509 firstBlockLength := uint32((prefixLen + blockSize - 1) / blockSize * blockSize)
510 firstBlock := c.packetData[:firstBlockLength]
511 if _, err := io.ReadFull(r, firstBlock); err != nil {
515 c.oracleCamouflage = maxPacket + 4 + c.macSize - firstBlockLength
517 c.decrypter.CryptBlocks(firstBlock, firstBlock)
518 length := binary.BigEndian.Uint32(firstBlock[:4])
519 if length > maxPacket {
520 return nil, cbcError("ssh: packet too large")
522 if length+4 < maxUInt32(cbcMinPacketSize, blockSize) {
523 // The minimum size of a packet is 16 (or the cipher block size, whichever
525 return nil, cbcError("ssh: packet too small")
527 // The length of the packet (including the length field but not the MAC) must
528 // be a multiple of the block size or 8, whichever is larger.
529 if (length+4)%maxUInt32(cbcMinPacketSizeMultiple, blockSize) != 0 {
530 return nil, cbcError("ssh: invalid packet length multiple")
533 paddingLength := uint32(firstBlock[4])
534 if paddingLength < cbcMinPaddingSize || length <= paddingLength+1 {
535 return nil, cbcError("ssh: invalid packet length")
538 // Positions within the c.packetData buffer:
539 macStart := 4 + length
540 paddingStart := macStart - paddingLength
542 // Entire packet size, starting before length, ending at end of mac.
543 entirePacketSize := macStart + c.macSize
545 // Ensure c.packetData is large enough for the entire packet data.
546 if uint32(cap(c.packetData)) < entirePacketSize {
547 // Still need to upsize and copy, but this should be rare at runtime, only
548 // on upsizing the packetData buffer.
549 c.packetData = make([]byte, entirePacketSize)
550 copy(c.packetData, firstBlock)
552 c.packetData = c.packetData[:entirePacketSize]
555 n, err := io.ReadFull(r, c.packetData[firstBlockLength:])
559 c.oracleCamouflage -= uint32(n)
561 remainingCrypted := c.packetData[firstBlockLength:macStart]
562 c.decrypter.CryptBlocks(remainingCrypted, remainingCrypted)
564 mac := c.packetData[macStart:]
567 binary.BigEndian.PutUint32(c.seqNumBytes[:], seqNum)
568 c.mac.Write(c.seqNumBytes[:])
569 c.mac.Write(c.packetData[:macStart])
570 c.macResult = c.mac.Sum(c.macResult[:0])
571 if subtle.ConstantTimeCompare(c.macResult, mac) != 1 {
572 return nil, cbcError("ssh: MAC failure")
576 return c.packetData[prefixLen:paddingStart], nil
579 func (c *cbcCipher) writePacket(seqNum uint32, w io.Writer, rand io.Reader, packet []byte) error {
580 effectiveBlockSize := maxUInt32(cbcMinPacketSizeMultiple, c.encrypter.BlockSize())
582 // Length of encrypted portion of the packet (header, payload, padding).
583 // Enforce minimum padding and packet size.
584 encLength := maxUInt32(prefixLen+len(packet)+cbcMinPaddingSize, cbcMinPaddingSize)
585 // Enforce block size.
586 encLength = (encLength + effectiveBlockSize - 1) / effectiveBlockSize * effectiveBlockSize
588 length := encLength - 4
589 paddingLength := int(length) - (1 + len(packet))
591 // Overall buffer contains: header, payload, padding, mac.
592 // Space for the MAC is reserved in the capacity but not the slice length.
593 bufferSize := encLength + c.macSize
594 if uint32(cap(c.packetData)) < bufferSize {
595 c.packetData = make([]byte, encLength, bufferSize)
597 c.packetData = c.packetData[:encLength]
603 binary.BigEndian.PutUint32(p, length)
605 p[0] = byte(paddingLength)
613 if _, err := io.ReadFull(rand, p); err != nil {
619 binary.BigEndian.PutUint32(c.seqNumBytes[:], seqNum)
620 c.mac.Write(c.seqNumBytes[:])
621 c.mac.Write(c.packetData)
622 // The MAC is now appended into the capacity reserved for it earlier.
623 c.packetData = c.mac.Sum(c.packetData)
626 c.encrypter.CryptBlocks(c.packetData[:encLength], c.packetData[:encLength])
628 if _, err := w.Write(c.packetData); err != nil {
635 const chacha20Poly1305ID = "chacha20-poly1305@openssh.com"
637 // chacha20Poly1305Cipher implements the chacha20-poly1305@openssh.com
638 // AEAD, which is described here:
640 // https://tools.ietf.org/html/draft-josefsson-ssh-chacha20-poly1305-openssh-00
642 // the methods here also implement padding, which RFC4253 Section 6
643 // also requires of stream ciphers.
644 type chacha20Poly1305Cipher struct {
650 func newChaCha20Cipher(key, unusedIV, unusedMACKey []byte, unusedAlgs directionAlgorithms) (packetCipher, error) {
655 c := &chacha20Poly1305Cipher{
656 buf: make([]byte, 256),
659 for i := range c.contentKey {
660 c.contentKey[i] = binary.LittleEndian.Uint32(key[i*4 : (i+1)*4])
662 for i := range c.lengthKey {
663 c.lengthKey[i] = binary.LittleEndian.Uint32(key[(i+8)*4 : (i+9)*4])
668 func (c *chacha20Poly1305Cipher) readPacket(seqNum uint32, r io.Reader) ([]byte, error) {
669 nonce := [3]uint32{0, 0, bits.ReverseBytes32(seqNum)}
670 s := chacha20.New(c.contentKey, nonce)
672 s.XORKeyStream(polyKey[:], polyKey[:])
673 s.Advance() // skip next 32 bytes
675 encryptedLength := c.buf[:4]
676 if _, err := io.ReadFull(r, encryptedLength); err != nil {
681 chacha20.New(c.lengthKey, nonce).XORKeyStream(lenBytes[:], encryptedLength)
683 length := binary.BigEndian.Uint32(lenBytes[:])
684 if length > maxPacket {
685 return nil, errors.New("ssh: invalid packet length, packet too large")
688 contentEnd := 4 + length
689 packetEnd := contentEnd + poly1305.TagSize
690 if uint32(cap(c.buf)) < packetEnd {
691 c.buf = make([]byte, packetEnd)
692 copy(c.buf[:], encryptedLength)
694 c.buf = c.buf[:packetEnd]
697 if _, err := io.ReadFull(r, c.buf[4:packetEnd]); err != nil {
701 var mac [poly1305.TagSize]byte
702 copy(mac[:], c.buf[contentEnd:packetEnd])
703 if !poly1305.Verify(&mac, c.buf[:contentEnd], &polyKey) {
704 return nil, errors.New("ssh: MAC failure")
707 plain := c.buf[4:contentEnd]
708 s.XORKeyStream(plain, plain)
712 // padding is a byte, so it automatically satisfies
713 // the maximum size, which is 255.
714 return nil, fmt.Errorf("ssh: illegal padding %d", padding)
717 if int(padding)+1 >= len(plain) {
718 return nil, fmt.Errorf("ssh: padding %d too large", padding)
721 plain = plain[1 : len(plain)-int(padding)]
726 func (c *chacha20Poly1305Cipher) writePacket(seqNum uint32, w io.Writer, rand io.Reader, payload []byte) error {
727 nonce := [3]uint32{0, 0, bits.ReverseBytes32(seqNum)}
728 s := chacha20.New(c.contentKey, nonce)
730 s.XORKeyStream(polyKey[:], polyKey[:])
731 s.Advance() // skip next 32 bytes
733 // There is no blocksize, so fall back to multiple of 8 byte
734 // padding, as described in RFC 4253, Sec 6.
735 const packetSizeMultiple = 8
737 padding := packetSizeMultiple - (1+len(payload))%packetSizeMultiple
739 padding += packetSizeMultiple
742 // size (4 bytes), padding (1), payload, padding, tag.
743 totalLength := 4 + 1 + len(payload) + padding + poly1305.TagSize
744 if cap(c.buf) < totalLength {
745 c.buf = make([]byte, totalLength)
747 c.buf = c.buf[:totalLength]
750 binary.BigEndian.PutUint32(c.buf, uint32(1+len(payload)+padding))
751 chacha20.New(c.lengthKey, nonce).XORKeyStream(c.buf, c.buf[:4])
752 c.buf[4] = byte(padding)
753 copy(c.buf[5:], payload)
754 packetEnd := 5 + len(payload) + padding
755 if _, err := io.ReadFull(rand, c.buf[5+len(payload):packetEnd]); err != nil {
759 s.XORKeyStream(c.buf[4:], c.buf[4:packetEnd])
761 var mac [poly1305.TagSize]byte
762 poly1305.Sum(&mac, c.buf[:packetEnd], &polyKey)
764 copy(c.buf[packetEnd:], mac[:])
766 if _, err := w.Write(c.buf); err != nil {