Commit fd397855 authored by Dave Cheney's avatar Dave Cheney Committed by Adam Langley

exp/ssh: refactor halfConnection to transport

        This CL generalises the pair of halfConnection members that the
        serverConn holds into a single transport struct that is shared by
        both Server and Client, see also CL 5037047.

        This CL is a replacement for 5040046 which I closed by accident.

R=agl, bradfitz
CC=golang-dev
https://golang.org/cl/5075042
parent 3a013f11
...@@ -97,7 +97,7 @@ func (c *channel) Accept() os.Error { ...@@ -97,7 +97,7 @@ func (c *channel) Accept() os.Error {
MyWindow: c.myWindow, MyWindow: c.myWindow,
MaxPacketSize: c.maxPacketSize, MaxPacketSize: c.maxPacketSize,
} }
return c.serverConn.out.writePacket(marshal(msgChannelOpenConfirm, confirm)) return c.serverConn.writePacket(marshal(msgChannelOpenConfirm, confirm))
} }
func (c *channel) Reject(reason RejectionReason, message string) os.Error { func (c *channel) Reject(reason RejectionReason, message string) os.Error {
...@@ -114,7 +114,7 @@ func (c *channel) Reject(reason RejectionReason, message string) os.Error { ...@@ -114,7 +114,7 @@ func (c *channel) Reject(reason RejectionReason, message string) os.Error {
Message: message, Message: message,
Language: "en", Language: "en",
} }
return c.serverConn.out.writePacket(marshal(msgChannelOpenFailure, reject)) return c.serverConn.writePacket(marshal(msgChannelOpenFailure, reject))
} }
func (c *channel) handlePacket(packet interface{}) { func (c *channel) handlePacket(packet interface{}) {
...@@ -180,7 +180,7 @@ func (c *channel) Read(data []byte) (n int, err os.Error) { ...@@ -180,7 +180,7 @@ func (c *channel) Read(data []byte) (n int, err os.Error) {
PeersId: c.theirId, PeersId: c.theirId,
AdditionalBytes: uint32(len(c.pendingData)) - c.myWindow, AdditionalBytes: uint32(len(c.pendingData)) - c.myWindow,
}) })
if err := c.serverConn.out.writePacket(packet); err != nil { if err := c.serverConn.writePacket(packet); err != nil {
return 0, err return 0, err
} }
} }
...@@ -254,7 +254,7 @@ func (c *channel) Write(data []byte) (n int, err os.Error) { ...@@ -254,7 +254,7 @@ func (c *channel) Write(data []byte) (n int, err os.Error) {
copy(packet[9:], todo) copy(packet[9:], todo)
c.serverConn.lock.Lock() c.serverConn.lock.Lock()
if err = c.serverConn.out.writePacket(packet); err != nil { if err = c.serverConn.writePacket(packet); err != nil {
c.serverConn.lock.Unlock() c.serverConn.lock.Unlock()
return return
} }
...@@ -283,7 +283,7 @@ func (c *channel) Close() os.Error { ...@@ -283,7 +283,7 @@ func (c *channel) Close() os.Error {
closeMsg := channelCloseMsg{ closeMsg := channelCloseMsg{
PeersId: c.theirId, PeersId: c.theirId,
} }
return c.serverConn.out.writePacket(marshal(msgChannelClose, closeMsg)) return c.serverConn.writePacket(marshal(msgChannelClose, closeMsg))
} }
func (c *channel) AckRequest(ok bool) os.Error { func (c *channel) AckRequest(ok bool) os.Error {
...@@ -298,12 +298,12 @@ func (c *channel) AckRequest(ok bool) os.Error { ...@@ -298,12 +298,12 @@ func (c *channel) AckRequest(ok bool) os.Error {
ack := channelRequestSuccessMsg{ ack := channelRequestSuccessMsg{
PeersId: c.theirId, PeersId: c.theirId,
} }
return c.serverConn.out.writePacket(marshal(msgChannelSuccess, ack)) return c.serverConn.writePacket(marshal(msgChannelSuccess, ack))
} else { } else {
ack := channelRequestFailureMsg{ ack := channelRequestFailureMsg{
PeersId: c.theirId, PeersId: c.theirId,
} }
return c.serverConn.out.writePacket(marshal(msgChannelFailure, ack)) return c.serverConn.writePacket(marshal(msgChannelFailure, ack))
} }
panic("unreachable") panic("unreachable")
} }
......
...@@ -50,7 +50,7 @@ func findCommonAlgorithm(clientAlgos []string, serverAlgos []string) (commonAlgo ...@@ -50,7 +50,7 @@ func findCommonAlgorithm(clientAlgos []string, serverAlgos []string) (commonAlgo
return return
} }
func findAgreedAlgorithms(clientToServer, serverToClient *halfConnection, clientKexInit, serverKexInit *kexInitMsg) (kexAlgo, hostKeyAlgo string, ok bool) { func findAgreedAlgorithms(clientToServer, serverToClient *transport, clientKexInit, serverKexInit *kexInitMsg) (kexAlgo, hostKeyAlgo string, ok bool) {
kexAlgo, ok = findCommonAlgorithm(clientKexInit.KexAlgos, serverKexInit.KexAlgos) kexAlgo, ok = findCommonAlgorithm(clientKexInit.KexAlgos, serverKexInit.KexAlgos)
if !ok { if !ok {
return return
......
...@@ -129,7 +129,7 @@ const maxCachedPubKeys = 16 ...@@ -129,7 +129,7 @@ const maxCachedPubKeys = 16
type ServerConnection struct { type ServerConnection struct {
Server *Server Server *Server
in, out *halfConnection *transport
channels map[uint32]*channel channels map[uint32]*channel
nextChanId uint32 nextChanId uint32
...@@ -174,7 +174,7 @@ type handshakeMagics struct { ...@@ -174,7 +174,7 @@ type handshakeMagics struct {
// kexDH performs Diffie-Hellman key agreement on a ServerConnection. The // kexDH performs Diffie-Hellman key agreement on a ServerConnection. The
// returned values are given the same names as in RFC 4253, section 8. // returned values are given the same names as in RFC 4253, section 8.
func (s *ServerConnection) kexDH(group *dhGroup, hashFunc crypto.Hash, magics *handshakeMagics, hostKeyAlgo string) (H, K []byte, err os.Error) { func (s *ServerConnection) kexDH(group *dhGroup, hashFunc crypto.Hash, magics *handshakeMagics, hostKeyAlgo string) (H, K []byte, err os.Error) {
packet, err := s.in.readPacket() packet, err := s.readPacket()
if err != nil { if err != nil {
return return
} }
...@@ -241,7 +241,7 @@ func (s *ServerConnection) kexDH(group *dhGroup, hashFunc crypto.Hash, magics *h ...@@ -241,7 +241,7 @@ func (s *ServerConnection) kexDH(group *dhGroup, hashFunc crypto.Hash, magics *h
} }
packet = marshal(msgKexDHReply, kexDHReply) packet = marshal(msgKexDHReply, kexDHReply)
err = s.out.writePacket(packet) err = s.writePacket(packet)
return return
} }
...@@ -292,14 +292,30 @@ func buildDataSignedForAuth(sessionId []byte, req userAuthRequestMsg, algo, pubK ...@@ -292,14 +292,30 @@ func buildDataSignedForAuth(sessionId []byte, req userAuthRequestMsg, algo, pubK
// Handshake performs an SSH transport and client authentication on the given ServerConnection. // Handshake performs an SSH transport and client authentication on the given ServerConnection.
func (s *ServerConnection) Handshake(conn net.Conn) os.Error { func (s *ServerConnection) Handshake(conn net.Conn) os.Error {
var magics handshakeMagics var magics handshakeMagics
inBuf := bufio.NewReader(conn) s.transport = &transport{
reader: reader{
_, err := conn.Write(serverVersion) Reader: bufio.NewReader(conn),
if err != nil { },
writer: writer{
Writer: bufio.NewWriter(conn),
rand: rand.Reader,
},
Close: func() os.Error {
return conn.Close()
},
}
if _, err := conn.Write(serverVersion); err != nil {
return err return err
} }
magics.serverVersion = serverVersion[:len(serverVersion)-2] magics.serverVersion = serverVersion[:len(serverVersion)-2]
version, ok := readVersion(s.transport)
if !ok {
return os.NewError("failed to read version string from client")
}
magics.clientVersion = version
serverKexInit := kexInitMsg{ serverKexInit := kexInitMsg{
KexAlgos: supportedKexAlgos, KexAlgos: supportedKexAlgos,
ServerHostKeyAlgos: supportedHostKeyAlgos, ServerHostKeyAlgos: supportedHostKeyAlgos,
...@@ -313,28 +329,15 @@ func (s *ServerConnection) Handshake(conn net.Conn) os.Error { ...@@ -313,28 +329,15 @@ func (s *ServerConnection) Handshake(conn net.Conn) os.Error {
kexInitPacket := marshal(msgKexInit, serverKexInit) kexInitPacket := marshal(msgKexInit, serverKexInit)
magics.serverKexInit = kexInitPacket magics.serverKexInit = kexInitPacket
var out halfConnection if err := s.writePacket(kexInitPacket); err != nil {
out.out = conn
out.rand = rand.Reader
s.out = &out
err = out.writePacket(kexInitPacket)
if err != nil {
return err return err
} }
version, ok := readVersion(inBuf) packet, err := s.readPacket()
if !ok {
return os.NewError("failed to read version string from client")
}
magics.clientVersion = version
var in halfConnection
in.in = inBuf
s.in = &in
packet, err := in.readPacket()
if err != nil { if err != nil {
return err return err
} }
magics.clientKexInit = packet magics.clientKexInit = packet
var clientKexInit kexInitMsg var clientKexInit kexInitMsg
...@@ -342,7 +345,7 @@ func (s *ServerConnection) Handshake(conn net.Conn) os.Error { ...@@ -342,7 +345,7 @@ func (s *ServerConnection) Handshake(conn net.Conn) os.Error {
return err return err
} }
kexAlgo, hostKeyAlgo, ok := findAgreedAlgorithms(&in, &out, &clientKexInit, &serverKexInit) kexAlgo, hostKeyAlgo, ok := findAgreedAlgorithms(s.transport, s.transport, &clientKexInit, &serverKexInit)
if !ok { if !ok {
return os.NewError("ssh: no common algorithms") return os.NewError("ssh: no common algorithms")
} }
...@@ -350,7 +353,7 @@ func (s *ServerConnection) Handshake(conn net.Conn) os.Error { ...@@ -350,7 +353,7 @@ func (s *ServerConnection) Handshake(conn net.Conn) os.Error {
if clientKexInit.FirstKexFollows && kexAlgo != clientKexInit.KexAlgos[0] { if clientKexInit.FirstKexFollows && kexAlgo != clientKexInit.KexAlgos[0] {
// The client sent a Kex message for the wrong algorithm, // The client sent a Kex message for the wrong algorithm,
// which we have to ignore. // which we have to ignore.
_, err := in.readPacket() _, err := s.readPacket()
if err != nil { if err != nil {
return err return err
} }
...@@ -372,23 +375,23 @@ func (s *ServerConnection) Handshake(conn net.Conn) os.Error { ...@@ -372,23 +375,23 @@ func (s *ServerConnection) Handshake(conn net.Conn) os.Error {
} }
packet = []byte{msgNewKeys} packet = []byte{msgNewKeys}
if err = out.writePacket(packet); err != nil { if err = s.writePacket(packet); err != nil {
return err return err
} }
if err = out.setupKeys(serverKeys, K, H, H, hashFunc); err != nil { if err = s.transport.writer.setupKeys(serverKeys, K, H, H, hashFunc); err != nil {
return err return err
} }
if packet, err = in.readPacket(); err != nil { if packet, err = s.readPacket(); err != nil {
return err return err
} }
if packet[0] != msgNewKeys { if packet[0] != msgNewKeys {
return UnexpectedMessageError{msgNewKeys, packet[0]} return UnexpectedMessageError{msgNewKeys, packet[0]}
} }
in.setupKeys(clientKeys, K, H, H, hashFunc) s.transport.reader.setupKeys(clientKeys, K, H, H, hashFunc)
packet, err = in.readPacket() packet, err = s.readPacket()
if err != nil { if err != nil {
return err return err
} }
...@@ -405,7 +408,7 @@ func (s *ServerConnection) Handshake(conn net.Conn) os.Error { ...@@ -405,7 +408,7 @@ func (s *ServerConnection) Handshake(conn net.Conn) os.Error {
Service: serviceUserAuth, Service: serviceUserAuth,
} }
packet = marshal(msgServiceAccept, serviceAccept) packet = marshal(msgServiceAccept, serviceAccept)
if err = out.writePacket(packet); err != nil { if err = s.writePacket(packet); err != nil {
return err return err
} }
...@@ -455,7 +458,7 @@ func (s *ServerConnection) authenticate(H []byte) os.Error { ...@@ -455,7 +458,7 @@ func (s *ServerConnection) authenticate(H []byte) os.Error {
userAuthLoop: userAuthLoop:
for { for {
if packet, err = s.in.readPacket(); err != nil { if packet, err = s.readPacket(); err != nil {
return err return err
} }
if err = unmarshal(&userAuthReq, packet, msgUserAuthRequest); err != nil { if err = unmarshal(&userAuthReq, packet, msgUserAuthRequest); err != nil {
...@@ -519,7 +522,7 @@ userAuthLoop: ...@@ -519,7 +522,7 @@ userAuthLoop:
Algo: algo, Algo: algo,
PubKey: string(pubKey), PubKey: string(pubKey),
} }
if err = s.out.writePacket(marshal(msgUserAuthPubKeyOk, okMsg)); err != nil { if err = s.writePacket(marshal(msgUserAuthPubKeyOk, okMsg)); err != nil {
return err return err
} }
continue userAuthLoop continue userAuthLoop
...@@ -571,13 +574,13 @@ userAuthLoop: ...@@ -571,13 +574,13 @@ userAuthLoop:
return os.NewError("ssh: no authentication methods configured but NoClientAuth is also false") return os.NewError("ssh: no authentication methods configured but NoClientAuth is also false")
} }
if err = s.out.writePacket(marshal(msgUserAuthFailure, failureMsg)); err != nil { if err = s.writePacket(marshal(msgUserAuthFailure, failureMsg)); err != nil {
return err return err
} }
} }
packet = []byte{msgUserAuthSuccess} packet = []byte{msgUserAuthSuccess}
if err = s.out.writePacket(packet); err != nil { if err = s.writePacket(packet); err != nil {
return err return err
} }
...@@ -594,7 +597,7 @@ func (s *ServerConnection) Accept() (Channel, os.Error) { ...@@ -594,7 +597,7 @@ func (s *ServerConnection) Accept() (Channel, os.Error) {
} }
for { for {
packet, err := s.in.readPacket() packet, err := s.readPacket()
if err != nil { if err != nil {
s.lock.Lock() s.lock.Lock()
...@@ -697,7 +700,7 @@ func (s *ServerConnection) Accept() (Channel, os.Error) { ...@@ -697,7 +700,7 @@ func (s *ServerConnection) Accept() (Channel, os.Error) {
} }
if request.WantReply { if request.WantReply {
if err := s.out.writePacket([]byte{msgRequestFailure}); err != nil { if err := s.writePacket([]byte{msgRequestFailure}); err != nil {
return nil, err return nil, err
} }
} }
......
...@@ -13,56 +13,74 @@ import ( ...@@ -13,56 +13,74 @@ import (
"crypto/subtle" "crypto/subtle"
"hash" "hash"
"io" "io"
"net"
"os" "os"
) )
// halfConnection represents one direction of an SSH connection. It maintains const (
// the cipher state needed to process messages. paddingMultiple = 16 // TODO(dfc) does this need to be configurable?
type halfConnection struct { )
// Only one of these two will be non-nil
in *bufio.Reader // transport represents the SSH connection to the remote peer.
out net.Conn type transport struct {
reader
writer
rand io.Reader
cipherAlgo string cipherAlgo string
macAlgo string macAlgo string
compressionAlgo 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 paddingMultiple int
rand io.Reader
common
}
// common represents the cipher state needed to process messages in a single
// direction.
type common struct {
seqNum uint32 seqNum uint32
mac hash.Hash mac hash.Hash
cipher cipher.Stream cipher cipher.Stream
} }
func (hc *halfConnection) readOnePacket() (packet []byte, err os.Error) { // Read and decrypt a single packet from the remote peer.
var lengthBytes [5]byte func (r *reader) readOnePacket() ([]byte, os.Error) {
var lengthBytes = make([]byte, 5)
var macSize uint32
_, err = io.ReadFull(hc.in, lengthBytes[:]) if _, err := io.ReadFull(r, lengthBytes); err != nil {
if err != nil { return nil, err
return
} }
if hc.cipher != nil { if r.cipher != nil {
hc.cipher.XORKeyStream(lengthBytes[:], lengthBytes[:]) r.cipher.XORKeyStream(lengthBytes, lengthBytes)
} }
macSize := 0 if r.mac != nil {
if hc.mac != nil { r.mac.Reset()
hc.mac.Reset() seqNumBytes := []byte{
var seqNumBytes [4]byte byte(r.seqNum >> 24),
seqNumBytes[0] = byte(hc.seqNum >> 24) byte(r.seqNum >> 16),
seqNumBytes[1] = byte(hc.seqNum >> 16) byte(r.seqNum >> 8),
seqNumBytes[2] = byte(hc.seqNum >> 8) byte(r.seqNum),
seqNumBytes[3] = byte(hc.seqNum) }
hc.mac.Write(seqNumBytes[:]) r.mac.Write(seqNumBytes)
hc.mac.Write(lengthBytes[:]) r.mac.Write(lengthBytes)
macSize = hc.mac.Size() macSize = uint32(r.mac.Size())
} }
length := uint32(lengthBytes[0])<<24 | uint32(lengthBytes[1])<<16 | uint32(lengthBytes[2])<<8 | uint32(lengthBytes[3]) length := uint32(lengthBytes[0])<<24 | uint32(lengthBytes[1])<<16 | uint32(lengthBytes[2])<<8 | uint32(lengthBytes[3])
paddingLength := uint32(lengthBytes[4]) paddingLength := uint32(lengthBytes[4])
if length <= paddingLength+1 { if length <= paddingLength+1 {
...@@ -72,31 +90,30 @@ func (hc *halfConnection) readOnePacket() (packet []byte, err os.Error) { ...@@ -72,31 +90,30 @@ func (hc *halfConnection) readOnePacket() (packet []byte, err os.Error) {
return nil, os.NewError("packet too large") return nil, os.NewError("packet too large")
} }
packet = make([]byte, length-1+uint32(macSize)) packet := make([]byte, length-1+macSize)
_, err = io.ReadFull(hc.in, packet) if _, err := io.ReadFull(r, packet); err != nil {
if err != nil {
return nil, err return nil, err
} }
mac := packet[length-1:] mac := packet[length-1:]
if hc.cipher != nil { if r.cipher != nil {
hc.cipher.XORKeyStream(packet, packet[:length-1]) r.cipher.XORKeyStream(packet, packet[:length-1])
} }
if hc.mac != nil { if r.mac != nil {
hc.mac.Write(packet[:length-1]) r.mac.Write(packet[:length-1])
if subtle.ConstantTimeCompare(hc.mac.Sum(), mac) != 1 { if subtle.ConstantTimeCompare(r.mac.Sum(), mac) != 1 {
return nil, os.NewError("ssh: MAC failure") return nil, os.NewError("ssh: MAC failure")
} }
} }
hc.seqNum++ r.seqNum++
packet = packet[:length-paddingLength-1] return packet[:length-paddingLength-1], nil
return
} }
func (hc *halfConnection) readPacket() (packet []byte, err os.Error) { // Read and decrypt next packet discarding debug and noop messages.
func (t *transport) readPacket() ([]byte, os.Error) {
for { for {
packet, err := hc.readOnePacket() packet, err := t.readOnePacket()
if err != nil { if err != nil {
return nil, err return nil, err
} }
...@@ -107,119 +124,113 @@ func (hc *halfConnection) readPacket() (packet []byte, err os.Error) { ...@@ -107,119 +124,113 @@ func (hc *halfConnection) readPacket() (packet []byte, err os.Error) {
panic("unreachable") panic("unreachable")
} }
func (hc *halfConnection) writePacket(packet []byte) os.Error { // Encrypt and send a packet of data to the remote peer.
paddingMultiple := hc.paddingMultiple func (w *writer) writePacket(packet []byte) os.Error {
if paddingMultiple == 0 { paddingLength := paddingMultiple - (5+len(packet))%paddingMultiple
paddingMultiple = 8
}
paddingLength := paddingMultiple - (4+1+len(packet))%paddingMultiple
if paddingLength < 4 { if paddingLength < 4 {
paddingLength += paddingMultiple paddingLength += paddingMultiple
} }
var lengthBytes [5]byte
length := len(packet) + 1 + paddingLength length := len(packet) + 1 + paddingLength
lengthBytes[0] = byte(length >> 24) lengthBytes := []byte{
lengthBytes[1] = byte(length >> 16) byte(length >> 24),
lengthBytes[2] = byte(length >> 8) byte(length >> 16),
lengthBytes[3] = byte(length) byte(length >> 8),
lengthBytes[4] = byte(paddingLength) byte(length),
byte(paddingLength),
var padding [32]byte }
_, err := io.ReadFull(hc.rand, padding[:paddingLength]) padding := make([]byte, paddingLength)
_, err := io.ReadFull(w.rand, padding)
if err != nil { if err != nil {
return err return err
} }
if hc.mac != nil { if w.mac != nil {
hc.mac.Reset() w.mac.Reset()
var seqNumBytes [4]byte seqNumBytes := []byte{
seqNumBytes[0] = byte(hc.seqNum >> 24) byte(w.seqNum >> 24),
seqNumBytes[1] = byte(hc.seqNum >> 16) byte(w.seqNum >> 16),
seqNumBytes[2] = byte(hc.seqNum >> 8) byte(w.seqNum >> 8),
seqNumBytes[3] = byte(hc.seqNum) byte(w.seqNum),
hc.mac.Write(seqNumBytes[:]) }
hc.mac.Write(lengthBytes[:]) w.mac.Write(seqNumBytes)
hc.mac.Write(packet) w.mac.Write(lengthBytes)
hc.mac.Write(padding[:paddingLength]) w.mac.Write(packet)
w.mac.Write(padding)
} }
if hc.cipher != nil { // TODO(dfc) lengthBytes, packet and padding should be
hc.cipher.XORKeyStream(lengthBytes[:], lengthBytes[:]) // subslices of a single buffer
hc.cipher.XORKeyStream(packet, packet) if w.cipher != nil {
hc.cipher.XORKeyStream(padding[:], padding[:paddingLength]) w.cipher.XORKeyStream(lengthBytes, lengthBytes)
w.cipher.XORKeyStream(packet, packet)
w.cipher.XORKeyStream(padding, padding)
} }
_, err = hc.out.Write(lengthBytes[:]) if _, err := w.Write(lengthBytes); err != nil {
if err != nil {
return err return err
} }
_, err = hc.out.Write(packet) if _, err := w.Write(packet); err != nil {
if err != nil {
return err return err
} }
_, err = hc.out.Write(padding[:paddingLength]) if _, err := w.Write(padding); err != nil {
if err != nil {
return err return err
} }
if hc.mac != nil { if w.mac != nil {
_, err = hc.out.Write(hc.mac.Sum()) if _, err := w.Write(w.mac.Sum()); err != nil {
return err
}
} }
hc.seqNum++ if err := w.Flush(); err != nil {
return err
}
w.seqNum++
return err return err
} }
const ( type direction struct {
serverKeys = iota ivTag []byte
clientKeys 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'}}
) )
// setupServerKeys sets the cipher and MAC keys from K, H and sessionId, as // 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 // described in RFC 4253, section 6.4. direction should either be serverKeys
// (to setup server->client keys) or clientKeys (for client->server keys). // (to setup server->client keys) or clientKeys (for client->server keys).
func (hc *halfConnection) setupKeys(direction int, K, H, sessionId []byte, hashFunc crypto.Hash) os.Error { func (c *common) setupKeys(d direction, K, H, sessionId []byte, hashFunc crypto.Hash) os.Error {
h := hashFunc.New() h := hashFunc.New()
// We only support these algorithms for now.
if hc.cipherAlgo != cipherAES128CTR || hc.macAlgo != macSHA196 {
return os.NewError("ssh: setupServerKeys internal error")
}
blockSize := 16 blockSize := 16
keySize := 16 keySize := 16
macKeySize := 20 macKeySize := 20
var ivTag, keyTag, macKeyTag byte
if direction == serverKeys {
ivTag, keyTag, macKeyTag = 'B', 'D', 'F'
} else {
ivTag, keyTag, macKeyTag = 'A', 'C', 'E'
}
iv := make([]byte, blockSize) iv := make([]byte, blockSize)
key := make([]byte, keySize) key := make([]byte, keySize)
macKey := make([]byte, macKeySize) macKey := make([]byte, macKeySize)
generateKeyMaterial(iv, ivTag, K, H, sessionId, h) generateKeyMaterial(iv, d.ivTag, K, H, sessionId, h)
generateKeyMaterial(key, keyTag, K, H, sessionId, h) generateKeyMaterial(key, d.keyTag, K, H, sessionId, h)
generateKeyMaterial(macKey, macKeyTag, K, H, sessionId, h) generateKeyMaterial(macKey, d.macKeyTag, K, H, sessionId, h)
hc.mac = truncatingMAC{12, hmac.NewSHA1(macKey)} c.mac = truncatingMAC{12, hmac.NewSHA1(macKey)}
aes, err := aes.NewCipher(key) aes, err := aes.NewCipher(key)
if err != nil { if err != nil {
return err return err
} }
hc.cipher = cipher.NewCTR(aes, iv) c.cipher = cipher.NewCTR(aes, iv)
hc.paddingMultiple = 16
return nil return nil
} }
// generateKeyMaterial fills out with key material generated from tag, K, H // generateKeyMaterial fills out with key material generated from tag, K, H
// and sessionId, as specified in RFC 4253, section 7.2. // and sessionId, as specified in RFC 4253, section 7.2.
func generateKeyMaterial(out []byte, tag byte, K, H, sessionId []byte, h hash.Hash) { func generateKeyMaterial(out, tag []byte, K, H, sessionId []byte, h hash.Hash) {
var digestsSoFar []byte var digestsSoFar []byte
for len(out) > 0 { for len(out) > 0 {
...@@ -228,7 +239,7 @@ func generateKeyMaterial(out []byte, tag byte, K, H, sessionId []byte, h hash.Ha ...@@ -228,7 +239,7 @@ func generateKeyMaterial(out []byte, tag byte, K, H, sessionId []byte, h hash.Ha
h.Write(H) h.Write(H)
if len(digestsSoFar) == 0 { if len(digestsSoFar) == 0 {
h.Write([]byte{tag}) h.Write(tag)
h.Write(sessionId) h.Write(sessionId)
} else { } else {
h.Write(digestsSoFar) h.Write(digestsSoFar)
...@@ -273,16 +284,19 @@ func (t truncatingMAC) Size() int { ...@@ -273,16 +284,19 @@ func (t truncatingMAC) Size() int {
// while searching for the end of the version handshake. // while searching for the end of the version handshake.
const maxVersionStringBytes = 1024 const maxVersionStringBytes = 1024
func readVersion(r *bufio.Reader) (versionString []byte, ok bool) { // 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) versionString = make([]byte, 0, 64)
seenCR := false seenCR := false
var buf [1]byte
forEachByte: forEachByte:
for len(versionString) < maxVersionStringBytes { for len(versionString) < maxVersionStringBytes {
b, err := r.ReadByte() _, err := io.ReadFull(r, buf[:])
if err != nil { if err != nil {
return return
} }
b := buf[0]
if !seenCR { if !seenCR {
if b == '\r' { if b == '\r' {
......
// 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"
"bytes"
"testing"
)
func TestReadVersion(t *testing.T) {
buf := []byte(serverVersion)
result, ok := readVersion(bufio.NewReader(bytes.NewBuffer(buf)))
if !ok {
t.Error("readVersion didn't read version correctly")
}
if !bytes.Equal(buf[:len(buf)-2], result) {
t.Error("version read did not match expected")
}
}
func TestReadVersionTooLong(t *testing.T) {
buf := make([]byte, maxVersionStringBytes+1)
if _, ok := readVersion(bufio.NewReader(bytes.NewBuffer(buf))); ok {
t.Errorf("readVersion consumed %d bytes without error", len(buf))
}
}
func TestReadVersionWithoutCRLF(t *testing.T) {
buf := []byte(serverVersion)
buf = buf[:len(buf)-1]
if _, ok := readVersion(bufio.NewReader(bytes.NewBuffer(buf))); ok {
t.Error("readVersion did not notice \\n was missing")
}
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment