github.com/technosophos/deis@v1.7.1-0.20150915173815-f9005256004b/Godeps/_workspace/src/golang.org/x/crypto/ssh/cipher.go (about) 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/rc4" 11 "crypto/subtle" 12 "encoding/binary" 13 "errors" 14 "fmt" 15 "hash" 16 "io" 17 ) 18 19 const ( 20 packetSizeMultiple = 16 // TODO(huin) this should be determined by the cipher. 21 22 // RFC 4253 section 6.1 defines a minimum packet size of 32768 that implementations 23 // MUST be able to process (plus a few more kilobytes for padding and mac). The RFC 24 // indicates implementations SHOULD be able to handle larger packet sizes, but then 25 // waffles on about reasonable limits. 26 // 27 // OpenSSH caps their maxPacket at 256kB so we choose to do 28 // the same. maxPacket is also used to ensure that uint32 29 // length fields do not overflow, so it should remain well 30 // below 4G. 31 maxPacket = 256 * 1024 32 ) 33 34 // noneCipher implements cipher.Stream and provides no encryption. It is used 35 // by the transport before the first key-exchange. 36 type noneCipher struct{} 37 38 func (c noneCipher) XORKeyStream(dst, src []byte) { 39 copy(dst, src) 40 } 41 42 func newAESCTR(key, iv []byte) (cipher.Stream, error) { 43 c, err := aes.NewCipher(key) 44 if err != nil { 45 return nil, err 46 } 47 return cipher.NewCTR(c, iv), nil 48 } 49 50 func newRC4(key, iv []byte) (cipher.Stream, error) { 51 return rc4.NewCipher(key) 52 } 53 54 type streamCipherMode struct { 55 keySize int 56 ivSize int 57 skip int 58 createFunc func(key, iv []byte) (cipher.Stream, error) 59 } 60 61 func (c *streamCipherMode) createStream(key, iv []byte) (cipher.Stream, error) { 62 if len(key) < c.keySize { 63 panic("ssh: key length too small for cipher") 64 } 65 if len(iv) < c.ivSize { 66 panic("ssh: iv too small for cipher") 67 } 68 69 stream, err := c.createFunc(key[:c.keySize], iv[:c.ivSize]) 70 if err != nil { 71 return nil, err 72 } 73 74 var streamDump []byte 75 if c.skip > 0 { 76 streamDump = make([]byte, 512) 77 } 78 79 for remainingToDump := c.skip; remainingToDump > 0; { 80 dumpThisTime := remainingToDump 81 if dumpThisTime > len(streamDump) { 82 dumpThisTime = len(streamDump) 83 } 84 stream.XORKeyStream(streamDump[:dumpThisTime], streamDump[:dumpThisTime]) 85 remainingToDump -= dumpThisTime 86 } 87 88 return stream, nil 89 } 90 91 // cipherModes documents properties of supported ciphers. Ciphers not included 92 // are not supported and will not be negotiated, even if explicitly requested in 93 // ClientConfig.Crypto.Ciphers. 94 var cipherModes = map[string]*streamCipherMode{ 95 // Ciphers from RFC4344, which introduced many CTR-based ciphers. Algorithms 96 // are defined in the order specified in the RFC. 97 "aes128-ctr": {16, aes.BlockSize, 0, newAESCTR}, 98 "aes192-ctr": {24, aes.BlockSize, 0, newAESCTR}, 99 "aes256-ctr": {32, aes.BlockSize, 0, newAESCTR}, 100 101 // Ciphers from RFC4345, which introduces security-improved arcfour ciphers. 102 // They are defined in the order specified in the RFC. 103 "arcfour128": {16, 0, 1536, newRC4}, 104 "arcfour256": {32, 0, 1536, newRC4}, 105 106 // Cipher defined in RFC 4253, which describes SSH Transport Layer Protocol. 107 // Note that this cipher is not safe, as stated in RFC 4253: "Arcfour (and 108 // RC4) has problems with weak keys, and should be used with caution." 109 // RFC4345 introduces improved versions of Arcfour. 110 "arcfour": {16, 0, 0, newRC4}, 111 112 // AES-GCM is not a stream cipher, so it is constructed with a 113 // special case. If we add any more non-stream ciphers, we 114 // should invest a cleaner way to do this. 115 gcmCipherID: {16, 12, 0, nil}, 116 } 117 118 // prefixLen is the length of the packet prefix that contains the packet length 119 // and number of padding bytes. 120 const prefixLen = 5 121 122 // streamPacketCipher is a packetCipher using a stream cipher. 123 type streamPacketCipher struct { 124 mac hash.Hash 125 cipher cipher.Stream 126 127 // The following members are to avoid per-packet allocations. 128 prefix [prefixLen]byte 129 seqNumBytes [4]byte 130 padding [2 * packetSizeMultiple]byte 131 packetData []byte 132 macResult []byte 133 } 134 135 // readPacket reads and decrypt a single packet from the reader argument. 136 func (s *streamPacketCipher) readPacket(seqNum uint32, r io.Reader) ([]byte, error) { 137 if _, err := io.ReadFull(r, s.prefix[:]); err != nil { 138 return nil, err 139 } 140 141 s.cipher.XORKeyStream(s.prefix[:], s.prefix[:]) 142 length := binary.BigEndian.Uint32(s.prefix[0:4]) 143 paddingLength := uint32(s.prefix[4]) 144 145 var macSize uint32 146 if s.mac != nil { 147 s.mac.Reset() 148 binary.BigEndian.PutUint32(s.seqNumBytes[:], seqNum) 149 s.mac.Write(s.seqNumBytes[:]) 150 s.mac.Write(s.prefix[:]) 151 macSize = uint32(s.mac.Size()) 152 } 153 154 if length <= paddingLength+1 { 155 return nil, errors.New("ssh: invalid packet length, packet too small") 156 } 157 158 if length > maxPacket { 159 return nil, errors.New("ssh: invalid packet length, packet too large") 160 } 161 162 // the maxPacket check above ensures that length-1+macSize 163 // does not overflow. 164 if uint32(cap(s.packetData)) < length-1+macSize { 165 s.packetData = make([]byte, length-1+macSize) 166 } else { 167 s.packetData = s.packetData[:length-1+macSize] 168 } 169 170 if _, err := io.ReadFull(r, s.packetData); err != nil { 171 return nil, err 172 } 173 mac := s.packetData[length-1:] 174 data := s.packetData[:length-1] 175 s.cipher.XORKeyStream(data, data) 176 177 if s.mac != nil { 178 s.mac.Write(data) 179 s.macResult = s.mac.Sum(s.macResult[:0]) 180 if subtle.ConstantTimeCompare(s.macResult, mac) != 1 { 181 return nil, errors.New("ssh: MAC failure") 182 } 183 } 184 185 return s.packetData[:length-paddingLength-1], nil 186 } 187 188 // writePacket encrypts and sends a packet of data to the writer argument 189 func (s *streamPacketCipher) writePacket(seqNum uint32, w io.Writer, rand io.Reader, packet []byte) error { 190 if len(packet) > maxPacket { 191 return errors.New("ssh: packet too large") 192 } 193 194 paddingLength := packetSizeMultiple - (prefixLen+len(packet))%packetSizeMultiple 195 if paddingLength < 4 { 196 paddingLength += packetSizeMultiple 197 } 198 199 length := len(packet) + 1 + paddingLength 200 binary.BigEndian.PutUint32(s.prefix[:], uint32(length)) 201 s.prefix[4] = byte(paddingLength) 202 padding := s.padding[:paddingLength] 203 if _, err := io.ReadFull(rand, padding); err != nil { 204 return err 205 } 206 207 if s.mac != nil { 208 s.mac.Reset() 209 binary.BigEndian.PutUint32(s.seqNumBytes[:], seqNum) 210 s.mac.Write(s.seqNumBytes[:]) 211 s.mac.Write(s.prefix[:]) 212 s.mac.Write(packet) 213 s.mac.Write(padding) 214 } 215 216 s.cipher.XORKeyStream(s.prefix[:], s.prefix[:]) 217 s.cipher.XORKeyStream(packet, packet) 218 s.cipher.XORKeyStream(padding, padding) 219 220 if _, err := w.Write(s.prefix[:]); err != nil { 221 return err 222 } 223 if _, err := w.Write(packet); err != nil { 224 return err 225 } 226 if _, err := w.Write(padding); err != nil { 227 return err 228 } 229 230 if s.mac != nil { 231 s.macResult = s.mac.Sum(s.macResult[:0]) 232 if _, err := w.Write(s.macResult); err != nil { 233 return err 234 } 235 } 236 237 return nil 238 } 239 240 type gcmCipher struct { 241 aead cipher.AEAD 242 prefix [4]byte 243 iv []byte 244 buf []byte 245 } 246 247 func newGCMCipher(iv, key, macKey []byte) (packetCipher, error) { 248 c, err := aes.NewCipher(key) 249 if err != nil { 250 return nil, err 251 } 252 253 aead, err := cipher.NewGCM(c) 254 if err != nil { 255 return nil, err 256 } 257 258 return &gcmCipher{ 259 aead: aead, 260 iv: iv, 261 }, nil 262 } 263 264 const gcmTagSize = 16 265 266 func (c *gcmCipher) writePacket(seqNum uint32, w io.Writer, rand io.Reader, packet []byte) error { 267 // Pad out to multiple of 16 bytes. This is different from the 268 // stream cipher because that encrypts the length too. 269 padding := byte(packetSizeMultiple - (1+len(packet))%packetSizeMultiple) 270 if padding < 4 { 271 padding += packetSizeMultiple 272 } 273 274 length := uint32(len(packet) + int(padding) + 1) 275 binary.BigEndian.PutUint32(c.prefix[:], length) 276 if _, err := w.Write(c.prefix[:]); err != nil { 277 return err 278 } 279 280 if cap(c.buf) < int(length) { 281 c.buf = make([]byte, length) 282 } else { 283 c.buf = c.buf[:length] 284 } 285 286 c.buf[0] = padding 287 copy(c.buf[1:], packet) 288 if _, err := io.ReadFull(rand, c.buf[1+len(packet):]); err != nil { 289 return err 290 } 291 c.buf = c.aead.Seal(c.buf[:0], c.iv, c.buf, c.prefix[:]) 292 if _, err := w.Write(c.buf); err != nil { 293 return err 294 } 295 c.incIV() 296 297 return nil 298 } 299 300 func (c *gcmCipher) incIV() { 301 for i := 4 + 7; i >= 4; i-- { 302 c.iv[i]++ 303 if c.iv[i] != 0 { 304 break 305 } 306 } 307 } 308 309 func (c *gcmCipher) readPacket(seqNum uint32, r io.Reader) ([]byte, error) { 310 if _, err := io.ReadFull(r, c.prefix[:]); err != nil { 311 return nil, err 312 } 313 length := binary.BigEndian.Uint32(c.prefix[:]) 314 if length > maxPacket { 315 return nil, errors.New("ssh: max packet length exceeded.") 316 } 317 318 if cap(c.buf) < int(length+gcmTagSize) { 319 c.buf = make([]byte, length+gcmTagSize) 320 } else { 321 c.buf = c.buf[:length+gcmTagSize] 322 } 323 324 if _, err := io.ReadFull(r, c.buf); err != nil { 325 return nil, err 326 } 327 328 plain, err := c.aead.Open(c.buf[:0], c.iv, c.buf, c.prefix[:]) 329 if err != nil { 330 return nil, err 331 } 332 c.incIV() 333 334 padding := plain[0] 335 if padding < 4 || padding >= 20 { 336 return nil, fmt.Errorf("ssh: illegal padding %d", padding) 337 } 338 339 if int(padding+1) >= len(plain) { 340 return nil, fmt.Errorf("ssh: padding %d too large", padding) 341 } 342 plain = plain[1 : length-uint32(padding)] 343 return plain, nil 344 }