diff options
Diffstat (limited to 'src/pkg/exp/ssh/transport.go')
-rw-r--r-- | src/pkg/exp/ssh/transport.go | 322 |
1 files changed, 0 insertions, 322 deletions
diff --git a/src/pkg/exp/ssh/transport.go b/src/pkg/exp/ssh/transport.go deleted file mode 100644 index 5a474a9f2..000000000 --- a/src/pkg/exp/ssh/transport.go +++ /dev/null @@ -1,322 +0,0 @@ -// 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 ( - "bufio" - "crypto" - "crypto/aes" - "crypto/cipher" - "crypto/hmac" - "crypto/subtle" - "hash" - "io" - "os" -) - -const ( - paddingMultiple = 16 // TODO(dfc) does this need to be configurable? -) - -// transport represents the SSH connection to the remote peer. -type transport struct { - reader - writer - - cipherAlgo string - macAlgo string - compressionAlgo string - - Close func() os.Error -} - -// reader represents the incoming connection state. -type reader struct { - io.Reader - common -} - -// writer represnts the outgoing connection state. -type writer struct { - *bufio.Writer - paddingMultiple int - rand io.Reader - common -} - -// common represents the cipher state needed to process messages in a single -// direction. -type common struct { - seqNum uint32 - mac hash.Hash - cipher cipher.Stream -} - -// Read and decrypt a single packet from the remote peer. -func (r *reader) readOnePacket() ([]byte, os.Error) { - var lengthBytes = make([]byte, 5) - var macSize uint32 - - if _, err := io.ReadFull(r, lengthBytes); err != nil { - return nil, err - } - - if r.cipher != nil { - r.cipher.XORKeyStream(lengthBytes, lengthBytes) - } - - if r.mac != nil { - r.mac.Reset() - seqNumBytes := []byte{ - byte(r.seqNum >> 24), - byte(r.seqNum >> 16), - byte(r.seqNum >> 8), - byte(r.seqNum), - } - r.mac.Write(seqNumBytes) - r.mac.Write(lengthBytes) - macSize = uint32(r.mac.Size()) - } - - length := uint32(lengthBytes[0])<<24 | uint32(lengthBytes[1])<<16 | uint32(lengthBytes[2])<<8 | uint32(lengthBytes[3]) - paddingLength := uint32(lengthBytes[4]) - - if length <= paddingLength+1 { - return nil, os.NewError("invalid packet length") - } - if length > maxPacketSize { - return nil, os.NewError("packet too large") - } - - packet := make([]byte, length-1+macSize) - if _, err := io.ReadFull(r, packet); err != nil { - return nil, err - } - mac := packet[length-1:] - if r.cipher != nil { - r.cipher.XORKeyStream(packet, packet[:length-1]) - } - - if r.mac != nil { - r.mac.Write(packet[:length-1]) - if subtle.ConstantTimeCompare(r.mac.Sum(), mac) != 1 { - return nil, os.NewError("ssh: MAC failure") - } - } - - r.seqNum++ - return packet[:length-paddingLength-1], nil -} - -// Read and decrypt next packet discarding debug and noop messages. -func (t *transport) readPacket() ([]byte, os.Error) { - for { - packet, err := t.readOnePacket() - if err != nil { - return nil, err - } - if packet[0] != msgIgnore && packet[0] != msgDebug { - return packet, nil - } - } - panic("unreachable") -} - -// Encrypt and send a packet of data to the remote peer. -func (w *writer) writePacket(packet []byte) os.Error { - paddingLength := paddingMultiple - (5+len(packet))%paddingMultiple - if paddingLength < 4 { - paddingLength += paddingMultiple - } - - length := len(packet) + 1 + paddingLength - lengthBytes := []byte{ - byte(length >> 24), - byte(length >> 16), - byte(length >> 8), - byte(length), - byte(paddingLength), - } - padding := make([]byte, paddingLength) - _, err := io.ReadFull(w.rand, padding) - if err != nil { - return err - } - - if w.mac != nil { - w.mac.Reset() - seqNumBytes := []byte{ - byte(w.seqNum >> 24), - byte(w.seqNum >> 16), - byte(w.seqNum >> 8), - byte(w.seqNum), - } - w.mac.Write(seqNumBytes) - w.mac.Write(lengthBytes) - w.mac.Write(packet) - w.mac.Write(padding) - } - - // TODO(dfc) lengthBytes, packet and padding should be - // subslices of a single buffer - if w.cipher != nil { - w.cipher.XORKeyStream(lengthBytes, lengthBytes) - w.cipher.XORKeyStream(packet, packet) - w.cipher.XORKeyStream(padding, padding) - } - - if _, err := w.Write(lengthBytes); err != nil { - return err - } - if _, err := w.Write(packet); err != nil { - return err - } - if _, err := w.Write(padding); err != nil { - return err - } - - if w.mac != nil { - if _, err := w.Write(w.mac.Sum()); err != nil { - return err - } - } - - if err := w.Flush(); err != nil { - return err - } - w.seqNum++ - return err -} - -type direction struct { - ivTag []byte - keyTag []byte - macKeyTag []byte -} - -// TODO(dfc) can this be made a constant ? -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 K, 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 (c *common) setupKeys(d direction, K, H, sessionId []byte, hashFunc crypto.Hash) os.Error { - h := hashFunc.New() - - blockSize := 16 - keySize := 16 - macKeySize := 20 - - iv := make([]byte, blockSize) - key := make([]byte, keySize) - macKey := make([]byte, macKeySize) - generateKeyMaterial(iv, d.ivTag, K, H, sessionId, h) - generateKeyMaterial(key, d.keyTag, K, H, sessionId, h) - generateKeyMaterial(macKey, d.macKeyTag, K, H, sessionId, h) - - c.mac = truncatingMAC{12, hmac.NewSHA1(macKey)} - aes, err := aes.NewCipher(key) - if err != nil { - return err - } - c.cipher = cipher.NewCTR(aes, iv) - return nil -} - -// generateKeyMaterial fills out with key material generated from tag, K, H -// and sessionId, as specified in RFC 4253, section 7.2. -func generateKeyMaterial(out, tag []byte, K, H, sessionId []byte, h hash.Hash) { - var digestsSoFar []byte - - for len(out) > 0 { - h.Reset() - h.Write(K) - h.Write(H) - - if len(digestsSoFar) == 0 { - h.Write(tag) - h.Write(sessionId) - } else { - h.Write(digestsSoFar) - } - - digest := h.Sum() - n := copy(out, digest) - out = out[n:] - if len(out) > 0 { - digestsSoFar = append(digestsSoFar, digest...) - } - } -} - -// truncatingMAC wraps around a hash.Hash and truncates the output digest to -// a given size. -type truncatingMAC struct { - length int - hmac hash.Hash -} - -func (t truncatingMAC) Write(data []byte) (int, os.Error) { - return t.hmac.Write(data) -} - -func (t truncatingMAC) Sum() []byte { - digest := t.hmac.Sum() - return digest[:t.length] -} - -func (t truncatingMAC) Reset() { - t.hmac.Reset() -} - -func (t truncatingMAC) Size() int { - return t.length -} - -// maxVersionStringBytes is the maximum number of bytes that we'll accept as a -// version string. In the event that the client is talking a different protocol -// we need to set a limit otherwise we will keep using more and more memory -// while searching for the end of the version handshake. -const maxVersionStringBytes = 1024 - -// Read version string as specified by RFC 4253, section 4.2. -func readVersion(r io.Reader) (versionString []byte, ok bool) { - versionString = make([]byte, 0, 64) - seenCR := false - - var buf [1]byte -forEachByte: - for len(versionString) < maxVersionStringBytes { - _, err := io.ReadFull(r, buf[:]) - if err != nil { - return - } - b := buf[0] - - if !seenCR { - if b == '\r' { - seenCR = true - } - } else { - if b == '\n' { - ok = true - break forEachByte - } else { - seenCR = false - } - } - versionString = append(versionString, b) - } - - if ok { - // We need to remove the CR from versionString - versionString = versionString[:len(versionString)-1] - } - - return -} |