1
// Copyright 2011 The Go Authors. All rights reserved.
2
// Use of this source code is governed by a BSD-style
3
// license that can be found in the LICENSE file.
14
gcmCipherID = "aes128-gcm@openssh.com"
15
aes128cbcID = "aes128-cbc"
18
// packetConn represents a transport that implements packet based
20
type packetConn interface {
21
// Encrypt and send a packet of data to the remote peer.
22
writePacket(packet []byte) error
24
// Read a packet from the connection
25
readPacket() ([]byte, error)
27
// Close closes the write-side of the connection.
31
// transport is the keyingTransport that implements the SSH packet
33
type transport struct {
34
reader connectionState
35
writer connectionState
37
bufReader *bufio.Reader
38
bufWriter *bufio.Writer
43
// Initial H used for the session ID. Once assigned this does
44
// not change, even during subsequent key exchanges.
48
// getSessionID returns the ID of the SSH connection. The return value
49
// should not be modified.
50
func (t *transport) getSessionID() []byte {
51
if t.sessionID == nil {
52
panic("session ID not set yet")
57
// packetCipher represents a combination of SSH encryption/MAC
58
// protocol. A single instance should be used for one direction only.
59
type packetCipher interface {
60
// writePacket encrypts the packet and writes it to w. The
61
// contents of the packet are generally scrambled.
62
writePacket(seqnum uint32, w io.Writer, rand io.Reader, packet []byte) error
64
// readPacket reads and decrypts a packet of data. The
65
// returned packet may be overwritten by future calls of
67
readPacket(seqnum uint32, r io.Reader) ([]byte, error)
70
// connectionState represents one side (read or write) of the
71
// connection. This is necessary because each direction has its own
72
// keys, and can even have its own algorithms
73
type connectionState struct {
77
pendingKeyChange chan packetCipher
80
// prepareKeyChange sets up key material for a keychange. The key changes in
81
// both directions are triggered by reading and writing a msgNewKey packet
83
func (t *transport) prepareKeyChange(algs *algorithms, kexResult *kexResult) error {
84
if t.sessionID == nil {
85
t.sessionID = kexResult.H
88
kexResult.SessionID = t.sessionID
90
if ciph, err := newPacketCipher(t.reader.dir, algs.r, kexResult); err != nil {
93
t.reader.pendingKeyChange <- ciph
96
if ciph, err := newPacketCipher(t.writer.dir, algs.w, kexResult); err != nil {
99
t.writer.pendingKeyChange <- ciph
105
// Read and decrypt next packet.
106
func (t *transport) readPacket() ([]byte, error) {
107
return t.reader.readPacket(t.bufReader)
110
func (s *connectionState) readPacket(r *bufio.Reader) ([]byte, error) {
111
packet, err := s.packetCipher.readPacket(s.seqNum, r)
113
if err == nil && len(packet) == 0 {
114
err = errors.New("ssh: zero length packet")
117
if len(packet) > 0 && packet[0] == msgNewKeys {
119
case cipher := <-s.pendingKeyChange:
120
s.packetCipher = cipher
122
return nil, errors.New("ssh: got bogus newkeys message.")
126
// The packet may point to an internal buffer, so copy the
128
fresh := make([]byte, len(packet))
134
func (t *transport) writePacket(packet []byte) error {
135
return t.writer.writePacket(t.bufWriter, t.rand, packet)
138
func (s *connectionState) writePacket(w *bufio.Writer, rand io.Reader, packet []byte) error {
139
changeKeys := len(packet) > 0 && packet[0] == msgNewKeys
141
err := s.packetCipher.writePacket(s.seqNum, w, rand, packet)
145
if err = w.Flush(); err != nil {
151
case cipher := <-s.pendingKeyChange:
152
s.packetCipher = cipher
154
panic("ssh: no key material for msgNewKeys")
160
func newTransport(rwc io.ReadWriteCloser, rand io.Reader, isClient bool) *transport {
162
bufReader: bufio.NewReader(rwc),
163
bufWriter: bufio.NewWriter(rwc),
165
reader: connectionState{
166
packetCipher: &streamPacketCipher{cipher: noneCipher{}},
167
pendingKeyChange: make(chan packetCipher, 1),
169
writer: connectionState{
170
packetCipher: &streamPacketCipher{cipher: noneCipher{}},
171
pendingKeyChange: make(chan packetCipher, 1),
176
t.reader.dir = serverKeys
177
t.writer.dir = clientKeys
179
t.reader.dir = clientKeys
180
t.writer.dir = serverKeys
186
type direction struct {
193
serverKeys = direction{[]byte{'B'}, []byte{'D'}, []byte{'F'}}
194
clientKeys = direction{[]byte{'A'}, []byte{'C'}, []byte{'E'}}
197
// generateKeys generates key material for IV, MAC and encryption.
198
func generateKeys(d direction, algs directionAlgorithms, kex *kexResult) (iv, key, macKey []byte) {
199
cipherMode := cipherModes[algs.Cipher]
200
macMode := macModes[algs.MAC]
202
iv = make([]byte, cipherMode.ivSize)
203
key = make([]byte, cipherMode.keySize)
204
macKey = make([]byte, macMode.keySize)
206
generateKeyMaterial(iv, d.ivTag, kex)
207
generateKeyMaterial(key, d.keyTag, kex)
208
generateKeyMaterial(macKey, d.macKeyTag, kex)
212
// setupKeys sets the cipher and MAC keys from kex.K, kex.H and sessionId, as
213
// described in RFC 4253, section 6.4. direction should either be serverKeys
214
// (to setup server->client keys) or clientKeys (for client->server keys).
215
func newPacketCipher(d direction, algs directionAlgorithms, kex *kexResult) (packetCipher, error) {
216
iv, key, macKey := generateKeys(d, algs, kex)
218
if algs.Cipher == gcmCipherID {
219
return newGCMCipher(iv, key, macKey)
222
if algs.Cipher == aes128cbcID {
223
return newAESCBCCipher(iv, key, macKey, algs)
226
c := &streamPacketCipher{
227
mac: macModes[algs.MAC].new(macKey),
229
c.macResult = make([]byte, c.mac.Size())
232
c.cipher, err = cipherModes[algs.Cipher].createStream(key, iv)
240
// generateKeyMaterial fills out with key material generated from tag, K, H
241
// and sessionId, as specified in RFC 4253, section 7.2.
242
func generateKeyMaterial(out, tag []byte, r *kexResult) {
243
var digestsSoFar []byte
251
if len(digestsSoFar) == 0 {
255
h.Write(digestsSoFar)
259
n := copy(out, digest)
262
digestsSoFar = append(digestsSoFar, digest...)
267
const packageVersion = "SSH-2.0-Go"
269
// Sends and receives a version line. The versionLine string should
270
// be US ASCII, start with "SSH-2.0-", and should not include a
271
// newline. exchangeVersions returns the other side's version line.
272
func exchangeVersions(rw io.ReadWriter, versionLine []byte) (them []byte, err error) {
273
// Contrary to the RFC, we do not ignore lines that don't
274
// start with "SSH-2.0-" to make the library usable with
275
// nonconforming servers.
276
for _, c := range versionLine {
277
// The spec disallows non US-ASCII chars, and
278
// specifically forbids null chars.
280
return nil, errors.New("ssh: junk character in version line")
283
if _, err = rw.Write(append(versionLine, '\r', '\n')); err != nil {
287
them, err = readVersion(rw)
291
// maxVersionStringBytes is the maximum number of bytes that we'll
292
// accept as a version string. RFC 4253 section 4.2 limits this at 255
294
const maxVersionStringBytes = 255
296
// Read version string as specified by RFC 4253, section 4.2.
297
func readVersion(r io.Reader) ([]byte, error) {
298
versionString := make([]byte, 0, 64)
302
for len(versionString) < maxVersionStringBytes {
303
_, err := io.ReadFull(r, buf[:])
307
// The RFC says that the version should be terminated with \r\n
308
// but several SSH servers actually only send a \n.
314
// non ASCII chars are disallowed, but we are lenient,
315
// since Go doesn't use null-terminated strings.
317
// The RFC allows a comment after a space, however,
318
// all of it (version and comments) goes into the
320
versionString = append(versionString, buf[0])
324
return nil, errors.New("ssh: overflow reading version string")
327
// There might be a '\r' on the end which we should remove.
328
if len(versionString) > 0 && versionString[len(versionString)-1] == '\r' {
329
versionString = versionString[:len(versionString)-1]
331
return versionString, nil