123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549 |
- package ssh
- import (
- "crypto/aes"
- "crypto/cipher"
- "crypto/rc4"
- "crypto/subtle"
- "encoding/binary"
- "errors"
- "fmt"
- "hash"
- "io"
- "io/ioutil"
- )
- const (
- packetSizeMultiple = 16
-
-
-
-
-
-
-
-
-
- maxPacket = 256 * 1024
- )
- type noneCipher struct{}
- func (c noneCipher) XORKeyStream(dst, src []byte) {
- copy(dst, src)
- }
- func newAESCTR(key, iv []byte) (cipher.Stream, error) {
- c, err := aes.NewCipher(key)
- if err != nil {
- return nil, err
- }
- return cipher.NewCTR(c, iv), nil
- }
- func newRC4(key, iv []byte) (cipher.Stream, error) {
- return rc4.NewCipher(key)
- }
- type streamCipherMode struct {
- keySize int
- ivSize int
- skip int
- createFunc func(key, iv []byte) (cipher.Stream, error)
- }
- func (c *streamCipherMode) createStream(key, iv []byte) (cipher.Stream, error) {
- if len(key) < c.keySize {
- panic("ssh: key length too small for cipher")
- }
- if len(iv) < c.ivSize {
- panic("ssh: iv too small for cipher")
- }
- stream, err := c.createFunc(key[:c.keySize], iv[:c.ivSize])
- if err != nil {
- return nil, err
- }
- var streamDump []byte
- if c.skip > 0 {
- streamDump = make([]byte, 512)
- }
- for remainingToDump := c.skip; remainingToDump > 0; {
- dumpThisTime := remainingToDump
- if dumpThisTime > len(streamDump) {
- dumpThisTime = len(streamDump)
- }
- stream.XORKeyStream(streamDump[:dumpThisTime], streamDump[:dumpThisTime])
- remainingToDump -= dumpThisTime
- }
- return stream, nil
- }
- var cipherModes = map[string]*streamCipherMode{
-
-
- "aes128-ctr": {16, aes.BlockSize, 0, newAESCTR},
- "aes192-ctr": {24, aes.BlockSize, 0, newAESCTR},
- "aes256-ctr": {32, aes.BlockSize, 0, newAESCTR},
-
-
- "arcfour128": {16, 0, 1536, newRC4},
- "arcfour256": {32, 0, 1536, newRC4},
-
-
-
-
- "arcfour": {16, 0, 0, newRC4},
-
-
-
- gcmCipherID: {16, 12, 0, nil},
-
-
-
- }
- const prefixLen = 5
- type streamPacketCipher struct {
- mac hash.Hash
- cipher cipher.Stream
-
- prefix [prefixLen]byte
- seqNumBytes [4]byte
- padding [2 * packetSizeMultiple]byte
- packetData []byte
- macResult []byte
- }
- func (s *streamPacketCipher) readPacket(seqNum uint32, r io.Reader) ([]byte, error) {
- if _, err := io.ReadFull(r, s.prefix[:]); err != nil {
- return nil, err
- }
- s.cipher.XORKeyStream(s.prefix[:], s.prefix[:])
- length := binary.BigEndian.Uint32(s.prefix[0:4])
- paddingLength := uint32(s.prefix[4])
- var macSize uint32
- if s.mac != nil {
- s.mac.Reset()
- binary.BigEndian.PutUint32(s.seqNumBytes[:], seqNum)
- s.mac.Write(s.seqNumBytes[:])
- s.mac.Write(s.prefix[:])
- macSize = uint32(s.mac.Size())
- }
- if length <= paddingLength+1 {
- return nil, errors.New("ssh: invalid packet length, packet too small")
- }
- if length > maxPacket {
- return nil, errors.New("ssh: invalid packet length, packet too large")
- }
-
-
- if uint32(cap(s.packetData)) < length-1+macSize {
- s.packetData = make([]byte, length-1+macSize)
- } else {
- s.packetData = s.packetData[:length-1+macSize]
- }
- if _, err := io.ReadFull(r, s.packetData); err != nil {
- return nil, err
- }
- mac := s.packetData[length-1:]
- data := s.packetData[:length-1]
- s.cipher.XORKeyStream(data, data)
- if s.mac != nil {
- s.mac.Write(data)
- s.macResult = s.mac.Sum(s.macResult[:0])
- if subtle.ConstantTimeCompare(s.macResult, mac) != 1 {
- return nil, errors.New("ssh: MAC failure")
- }
- }
- return s.packetData[:length-paddingLength-1], nil
- }
- func (s *streamPacketCipher) writePacket(seqNum uint32, w io.Writer, rand io.Reader, packet []byte) error {
- if len(packet) > maxPacket {
- return errors.New("ssh: packet too large")
- }
- paddingLength := packetSizeMultiple - (prefixLen+len(packet))%packetSizeMultiple
- if paddingLength < 4 {
- paddingLength += packetSizeMultiple
- }
- length := len(packet) + 1 + paddingLength
- binary.BigEndian.PutUint32(s.prefix[:], uint32(length))
- s.prefix[4] = byte(paddingLength)
- padding := s.padding[:paddingLength]
- if _, err := io.ReadFull(rand, padding); err != nil {
- return err
- }
- if s.mac != nil {
- s.mac.Reset()
- binary.BigEndian.PutUint32(s.seqNumBytes[:], seqNum)
- s.mac.Write(s.seqNumBytes[:])
- s.mac.Write(s.prefix[:])
- s.mac.Write(packet)
- s.mac.Write(padding)
- }
- s.cipher.XORKeyStream(s.prefix[:], s.prefix[:])
- s.cipher.XORKeyStream(packet, packet)
- s.cipher.XORKeyStream(padding, padding)
- if _, err := w.Write(s.prefix[:]); err != nil {
- return err
- }
- if _, err := w.Write(packet); err != nil {
- return err
- }
- if _, err := w.Write(padding); err != nil {
- return err
- }
- if s.mac != nil {
- s.macResult = s.mac.Sum(s.macResult[:0])
- if _, err := w.Write(s.macResult); err != nil {
- return err
- }
- }
- return nil
- }
- type gcmCipher struct {
- aead cipher.AEAD
- prefix [4]byte
- iv []byte
- buf []byte
- }
- func newGCMCipher(iv, key, macKey []byte) (packetCipher, error) {
- c, err := aes.NewCipher(key)
- if err != nil {
- return nil, err
- }
- aead, err := cipher.NewGCM(c)
- if err != nil {
- return nil, err
- }
- return &gcmCipher{
- aead: aead,
- iv: iv,
- }, nil
- }
- const gcmTagSize = 16
- func (c *gcmCipher) writePacket(seqNum uint32, w io.Writer, rand io.Reader, packet []byte) error {
-
-
- padding := byte(packetSizeMultiple - (1+len(packet))%packetSizeMultiple)
- if padding < 4 {
- padding += packetSizeMultiple
- }
- length := uint32(len(packet) + int(padding) + 1)
- binary.BigEndian.PutUint32(c.prefix[:], length)
- if _, err := w.Write(c.prefix[:]); err != nil {
- return err
- }
- if cap(c.buf) < int(length) {
- c.buf = make([]byte, length)
- } else {
- c.buf = c.buf[:length]
- }
- c.buf[0] = padding
- copy(c.buf[1:], packet)
- if _, err := io.ReadFull(rand, c.buf[1+len(packet):]); err != nil {
- return err
- }
- c.buf = c.aead.Seal(c.buf[:0], c.iv, c.buf, c.prefix[:])
- if _, err := w.Write(c.buf); err != nil {
- return err
- }
- c.incIV()
- return nil
- }
- func (c *gcmCipher) incIV() {
- for i := 4 + 7; i >= 4; i-- {
- c.iv[i]++
- if c.iv[i] != 0 {
- break
- }
- }
- }
- func (c *gcmCipher) readPacket(seqNum uint32, r io.Reader) ([]byte, error) {
- if _, err := io.ReadFull(r, c.prefix[:]); err != nil {
- return nil, err
- }
- length := binary.BigEndian.Uint32(c.prefix[:])
- if length > maxPacket {
- return nil, errors.New("ssh: max packet length exceeded.")
- }
- if cap(c.buf) < int(length+gcmTagSize) {
- c.buf = make([]byte, length+gcmTagSize)
- } else {
- c.buf = c.buf[:length+gcmTagSize]
- }
- if _, err := io.ReadFull(r, c.buf); err != nil {
- return nil, err
- }
- plain, err := c.aead.Open(c.buf[:0], c.iv, c.buf, c.prefix[:])
- if err != nil {
- return nil, err
- }
- c.incIV()
- padding := plain[0]
- if padding < 4 || padding >= 20 {
- return nil, fmt.Errorf("ssh: illegal padding %d", padding)
- }
- if int(padding+1) >= len(plain) {
- return nil, fmt.Errorf("ssh: padding %d too large", padding)
- }
- plain = plain[1 : length-uint32(padding)]
- return plain, nil
- }
- type cbcCipher struct {
- mac hash.Hash
- macSize uint32
- decrypter cipher.BlockMode
- encrypter cipher.BlockMode
-
- seqNumBytes [4]byte
- packetData []byte
- macResult []byte
-
-
- oracleCamouflage uint32
- }
- func newAESCBCCipher(iv, key, macKey []byte, algs directionAlgorithms) (packetCipher, error) {
- c, err := aes.NewCipher(key)
- if err != nil {
- return nil, err
- }
- cbc := &cbcCipher{
- mac: macModes[algs.MAC].new(macKey),
- decrypter: cipher.NewCBCDecrypter(c, iv),
- encrypter: cipher.NewCBCEncrypter(c, iv),
- packetData: make([]byte, 1024),
- }
- if cbc.mac != nil {
- cbc.macSize = uint32(cbc.mac.Size())
- }
- return cbc, nil
- }
- func maxUInt32(a, b int) uint32 {
- if a > b {
- return uint32(a)
- }
- return uint32(b)
- }
- const (
- cbcMinPacketSizeMultiple = 8
- cbcMinPacketSize = 16
- cbcMinPaddingSize = 4
- )
- type cbcError string
- func (e cbcError) Error() string { return string(e) }
- func (c *cbcCipher) readPacket(seqNum uint32, r io.Reader) ([]byte, error) {
- p, err := c.readPacketLeaky(seqNum, r)
- if err != nil {
- if _, ok := err.(cbcError); ok {
-
-
-
-
- io.CopyN(ioutil.Discard, r, int64(c.oracleCamouflage))
- }
- }
- return p, err
- }
- func (c *cbcCipher) readPacketLeaky(seqNum uint32, r io.Reader) ([]byte, error) {
- blockSize := c.decrypter.BlockSize()
-
-
-
- firstBlockLength := uint32((prefixLen + blockSize - 1) / blockSize * blockSize)
- firstBlock := c.packetData[:firstBlockLength]
- if _, err := io.ReadFull(r, firstBlock); err != nil {
- return nil, err
- }
- c.oracleCamouflage = maxPacket + 4 + c.macSize - firstBlockLength
- c.decrypter.CryptBlocks(firstBlock, firstBlock)
- length := binary.BigEndian.Uint32(firstBlock[:4])
- if length > maxPacket {
- return nil, cbcError("ssh: packet too large")
- }
- if length+4 < maxUInt32(cbcMinPacketSize, blockSize) {
-
-
- return nil, cbcError("ssh: packet too small")
- }
-
-
- if (length+4)%maxUInt32(cbcMinPacketSizeMultiple, blockSize) != 0 {
- return nil, cbcError("ssh: invalid packet length multiple")
- }
- paddingLength := uint32(firstBlock[4])
- if paddingLength < cbcMinPaddingSize || length <= paddingLength+1 {
- return nil, cbcError("ssh: invalid packet length")
- }
-
- macStart := 4 + length
- paddingStart := macStart - paddingLength
-
- entirePacketSize := macStart + c.macSize
-
- if uint32(cap(c.packetData)) < entirePacketSize {
-
-
- c.packetData = make([]byte, entirePacketSize)
- copy(c.packetData, firstBlock)
- } else {
- c.packetData = c.packetData[:entirePacketSize]
- }
- if n, err := io.ReadFull(r, c.packetData[firstBlockLength:]); err != nil {
- return nil, err
- } else {
- c.oracleCamouflage -= uint32(n)
- }
- remainingCrypted := c.packetData[firstBlockLength:macStart]
- c.decrypter.CryptBlocks(remainingCrypted, remainingCrypted)
- mac := c.packetData[macStart:]
- if c.mac != nil {
- c.mac.Reset()
- binary.BigEndian.PutUint32(c.seqNumBytes[:], seqNum)
- c.mac.Write(c.seqNumBytes[:])
- c.mac.Write(c.packetData[:macStart])
- c.macResult = c.mac.Sum(c.macResult[:0])
- if subtle.ConstantTimeCompare(c.macResult, mac) != 1 {
- return nil, cbcError("ssh: MAC failure")
- }
- }
- return c.packetData[prefixLen:paddingStart], nil
- }
- func (c *cbcCipher) writePacket(seqNum uint32, w io.Writer, rand io.Reader, packet []byte) error {
- effectiveBlockSize := maxUInt32(cbcMinPacketSizeMultiple, c.encrypter.BlockSize())
-
-
- encLength := maxUInt32(prefixLen+len(packet)+cbcMinPaddingSize, cbcMinPaddingSize)
-
- encLength = (encLength + effectiveBlockSize - 1) / effectiveBlockSize * effectiveBlockSize
- length := encLength - 4
- paddingLength := int(length) - (1 + len(packet))
-
-
- bufferSize := encLength + c.macSize
- if uint32(cap(c.packetData)) < bufferSize {
- c.packetData = make([]byte, encLength, bufferSize)
- } else {
- c.packetData = c.packetData[:encLength]
- }
- p := c.packetData
-
- binary.BigEndian.PutUint32(p, length)
- p = p[4:]
- p[0] = byte(paddingLength)
-
- p = p[1:]
- copy(p, packet)
-
- p = p[len(packet):]
- if _, err := io.ReadFull(rand, p); err != nil {
- return err
- }
- if c.mac != nil {
- c.mac.Reset()
- binary.BigEndian.PutUint32(c.seqNumBytes[:], seqNum)
- c.mac.Write(c.seqNumBytes[:])
- c.mac.Write(c.packetData)
-
- c.packetData = c.mac.Sum(c.packetData)
- }
- c.encrypter.CryptBlocks(c.packetData[:encLength], c.packetData[:encLength])
- if _, err := w.Write(c.packetData); err != nil {
- return err
- }
- return nil
- }
|