// 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 sshimport ()// 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 = falseconst ( gcm128CipherID = "aes128-gcm@openssh.com" gcm256CipherID = "aes256-gcm@openssh.com" aes128cbcID = "aes128-cbc" tripledescbcID = "3des-cbc")// packetConn represents a transport that implements packet based// operations.type packetConn interface {// 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.type transport struct { reader connectionState writer connectionState bufReader *bufio.Reader bufWriter *bufio.Writer rand io.Reader isClient boolio.Closer}// packetCipher represents a combination of SSH encryption/MAC// protocol. A single instance should be used for one direction only.type packetCipher interface {// 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 algorithmstype connectionState struct {packetCipher seqNum uint32 dir direction pendingKeyChange chanpacketCipher}// 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 <- returnnil}func ( *transport) ( []byte, bool) {iflen() == 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 }iflen() == 0 || ([0] != msgIgnore && [0] != msgDebug) {break } }ifdebugTransport { .printPacket(, false) }return , }func ( *connectionState) ( *bufio.Reader) ([]byte, error) { , := .packetCipher.readCipherPacket(.seqNum, ) .seqNum++if == nil && len() == 0 { = errors.New("ssh: zero length packet") }iflen() > 0 {switch [0] {casemsgNewKeys:select {case := <-.pendingKeyChange: .packetCipher = default:returnnil, errors.New("ssh: got bogus newkeys message") }casemsgDisconnect:// 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.vardisconnectMsgif := Unmarshal(, &); != nil {returnnil, }returnnil, & } }// The packet may point to an internal buffer, so copy the // packet out here. := make([]byte, len())copy(, )return , }func ( *transport) ( []byte) error {ifdebugTransport { .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 newTransport( io.ReadWriteCloser, io.Reader, bool) *transport { := &transport{bufReader: bufio.NewReader(),bufWriter: bufio.NewWriter(),rand: ,reader: connectionState{packetCipher: &streamPacketCipher{cipher: noneCipher{}},pendingKeyChange: make(chanpacketCipher, 1), },writer: connectionState{packetCipher: &streamPacketCipher{cipher: noneCipher{}},pendingKeyChange: make(chanpacketCipher, 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 newPacketCipher( direction, directionAlgorithms, *kexResult) (packetCipher, error) { := cipherModes[.Cipher] := make([]byte, .ivSize) := make([]byte, .keySize)generateKeyMaterial(, .ivTag, )generateKeyMaterial(, .keyTag, )var []byteif !aeadCiphers[.Cipher] { := macModes[.MAC] = make([]byte, .keySize)generateKeyMaterial(, .macKeyTag, ) }returncipherModes[.Cipher].create(, , , )}// generateKeyMaterial fills out with key material generated from tag, K, H// and sessionId, as specified in RFC 4253, section 7.2.func generateKeyMaterial(, []byte, *kexResult) {var []byte := .Hash.New()forlen() > 0 { .Reset() .Write(.K) .Write(.H)iflen() == 0 { .Write() .Write(.SessionID) } else { .Write() } := .Sum(nil) := copy(, ) = [:]iflen() > 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.func exchangeVersions( io.ReadWriter, []byte) ( []byte, error) {// 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.for , := range {// The spec disallows non US-ASCII chars, and // specifically forbids null chars.if < 32 {returnnil, 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// charsconst maxVersionStringBytes = 255// Read version string as specified by RFC 4253, section 4.2.func readVersion( io.Reader) ([]byte, error) { := make([]byte, 0, 64)varboolvar [1]bytefor := 0; < maxVersionStringBytes; ++ { , := io.ReadFull(, [:])if != nil {returnnil, }// The RFC says that the version should be terminated with \r\n // but several SSH servers actually only send a \n.if [0] == '\n' {if !bytes.HasPrefix(, []byte("SSH-")) {// 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 } = truebreak }// 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 ! {returnnil, errors.New("ssh: overflow reading version string") }// There might be a '\r' on the end which we should remove.iflen() > 0 && [len()-1] == '\r' { = [:len()-1] }return , nil}
The pages are generated with Goldsv0.6.7. (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu.
PR and bug reports are welcome and can be submitted to the issue list.
Please follow @Go100and1 (reachable from the left QR code) to get the latest news of Golds.