Copyright 2011 The Go Authors. All rights reserved. Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.

package ssh

import (
	
	
	
	
	
)
debugTransport if set, will print packet types as they go over the wire. No message decoding is done, to minimize the impact on timing.
const debugTransport = false

const (
	gcmCipherID    = "aes128-gcm@openssh.com"
	aes128cbcID    = "aes128-cbc"
	tripledescbcID = "3des-cbc"
)
packetConn represents a transport that implements packet based operations.
Encrypt and send a packet of data to the remote peer.
	writePacket(packet []byte) error
Read a packet from the connection. The read is blocking, i.e. if error is nil, then the returned byte slice is always non-empty.
	readPacket() ([]byte, error)
Close closes the write-side of the connection.
	Close() error
}
transport is the keyingTransport that implements the SSH packet protocol.
packetCipher represents a combination of SSH encryption/MAC protocol. A single instance should be used for one direction only.
writeCipherPacket encrypts the packet and writes it to w. The contents of the packet are generally scrambled.
	writeCipherPacket(seqnum uint32, w io.Writer, rand io.Reader, packet []byte) error
readCipherPacket reads and decrypts a packet of data. The returned packet may be overwritten by future calls of readPacket.
	readCipherPacket(seqnum uint32, r io.Reader) ([]byte, error)
}
connectionState represents one side (read or write) of the connection. This is necessary because each direction has its own keys, and can even have its own algorithms
prepareKeyChange sets up key material for a keychange. The key changes in both directions are triggered by reading and writing a msgNewKey packet respectively.
func ( *transport) ( *algorithms,  *kexResult) error {
	,  := newPacketCipher(.reader.dir, .r, )
	if  != nil {
		return 
	}
	.reader.pendingKeyChange <- 

	,  = newPacketCipher(.writer.dir, .w, )
	if  != nil {
		return 
	}
	.writer.pendingKeyChange <- 

	return nil
}

func ( *transport) ( []byte,  bool) {
	if len() == 0 {
		return
	}
	 := "server"
	if .isClient {
		 = "client"
	}
	 := "read"
	if  {
		 = "write"
	}

	log.Println(, , [0])
}
Read and decrypt next packet.
func ( *transport) () ( []byte,  error) {
	for {
		,  = .reader.readPacket(.bufReader)
		if  != nil {
			break
		}
		if len() == 0 || ([0] != msgIgnore && [0] != msgDebug) {
			break
		}
	}
	if debugTransport {
		.printPacket(, false)
	}

	return , 
}

func ( *connectionState) ( *bufio.Reader) ([]byte, error) {
	,  := .packetCipher.readCipherPacket(.seqNum, )
	.seqNum++
	if  == nil && len() == 0 {
		 = errors.New("ssh: zero length packet")
	}

	if len() > 0 {
		switch [0] {
		case msgNewKeys:
			select {
			case  := <-.pendingKeyChange:
				.packetCipher = 
			default:
				return nil, errors.New("ssh: got bogus newkeys message")
			}

Transform a disconnect message into an error. Since this is lowest level at which we interpret message types, doing it here ensures that we don't have to handle it elsewhere.
			var  disconnectMsg
			if  := Unmarshal(, &);  != nil {
				return nil, 
			}
			return nil, &
		}
	}
The packet may point to an internal buffer, so copy the packet out here.
	 := make([]byte, len())
	copy(, )

	return , 
}

func ( *transport) ( []byte) error {
	if debugTransport {
		.printPacket(, true)
	}
	return .writer.writePacket(.bufWriter, .rand, )
}

func ( *connectionState) ( *bufio.Writer,  io.Reader,  []byte) error {
	 := len() > 0 && [0] == msgNewKeys

	 := .packetCipher.writeCipherPacket(.seqNum, , , )
	if  != nil {
		return 
	}
	if  = .Flush();  != nil {
		return 
	}
	.seqNum++
	if  {
		select {
		case  := <-.pendingKeyChange:
			.packetCipher = 
		default:
			panic("ssh: no key material for msgNewKeys")
		}
	}
	return 
}

func ( io.ReadWriteCloser,  io.Reader,  bool) *transport {
	 := &transport{
		bufReader: bufio.NewReader(),
		bufWriter: bufio.NewWriter(),
		rand:      ,
		reader: connectionState{
			packetCipher:     &streamPacketCipher{cipher: noneCipher{}},
			pendingKeyChange: make(chan packetCipher, 1),
		},
		writer: connectionState{
			packetCipher:     &streamPacketCipher{cipher: noneCipher{}},
			pendingKeyChange: make(chan packetCipher, 1),
		},
		Closer: ,
	}
	.isClient = 

	if  {
		.reader.dir = serverKeys
		.writer.dir = clientKeys
	} else {
		.reader.dir = clientKeys
		.writer.dir = serverKeys
	}

	return 
}

type direction struct {
	ivTag     []byte
	keyTag    []byte
	macKeyTag []byte
}

var (
	serverKeys = direction{[]byte{'B'}, []byte{'D'}, []byte{'F'}}
	clientKeys = direction{[]byte{'A'}, []byte{'C'}, []byte{'E'}}
)
setupKeys sets the cipher and MAC keys from kex.K, kex.H and sessionId, as described in RFC 4253, section 6.4. direction should either be serverKeys (to setup server->client keys) or clientKeys (for client->server keys).
func ( direction,  directionAlgorithms,  *kexResult) (packetCipher, error) {
	 := cipherModes[.Cipher]
	 := macModes[.MAC]

	 := make([]byte, .ivSize)
	 := make([]byte, .keySize)
	 := make([]byte, .keySize)

	generateKeyMaterial(, .ivTag, )
	generateKeyMaterial(, .keyTag, )
	generateKeyMaterial(, .macKeyTag, )

	return cipherModes[.Cipher].create(, , , )
}
generateKeyMaterial fills out with key material generated from tag, K, H and sessionId, as specified in RFC 4253, section 7.2.
func (,  []byte,  *kexResult) {
	var  []byte

	 := .Hash.New()
	for len() > 0 {
		.Reset()
		.Write(.K)
		.Write(.H)

		if len() == 0 {
			.Write()
			.Write(.SessionID)
		} else {
			.Write()
		}

		 := .Sum(nil)
		 := copy(, )
		 = [:]
		if len() > 0 {
			 = append(, ...)
		}
	}
}

const packageVersion = "SSH-2.0-Go"
Sends and receives a version line. The versionLine string should be US ASCII, start with "SSH-2.0-", and should not include a newline. exchangeVersions returns the other side's version line.
Contrary to the RFC, we do not ignore lines that don't start with "SSH-2.0-" to make the library usable with nonconforming servers.
The spec disallows non US-ASCII chars, and specifically forbids null chars.
		if  < 32 {
			return nil, errors.New("ssh: junk character in version line")
		}
	}
	if _,  = .Write(append(, '\r', '\n'));  != nil {
		return
	}

	,  = readVersion()
	return , 
}
maxVersionStringBytes is the maximum number of bytes that we'll accept as a version string. RFC 4253 section 4.2 limits this at 255 chars
Read version string as specified by RFC 4253, section 4.2.
func ( io.Reader) ([]byte, error) {
	 := make([]byte, 0, 64)
	var  bool
	var  [1]byte

	for  := 0;  < maxVersionStringBytes; ++ {
		,  := io.ReadFull(, [:])
		if  != nil {
			return nil, 
The RFC says that the version should be terminated with \r\n but several SSH servers actually only send a \n.
		if [0] == '\n' {
RFC 4253 says we need to ignore all version string lines except the one containing the SSH version (provided that all the lines do not exceed 255 bytes in total).
				 = [:0]
				continue
			}
			 = true
			break
		}
non ASCII chars are disallowed, but we are lenient, since Go doesn't use null-terminated strings.
The RFC allows a comment after a space, however, all of it (version and comments) goes into the session hash.
		 = append(, [0])
	}

	if ! {
		return nil, errors.New("ssh: overflow reading version string")
	}
There might be a '\r' on the end which we should remove.
	if len() > 0 && [len()-1] == '\r' {
		 = [:len()-1]
	}
	return , nil