OSDN Git Service

new repo
[bytom/vapor.git] / vendor / golang.org / x / crypto / ssh / transport.go
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.
4
5 package ssh
6
7 import (
8         "bufio"
9         "errors"
10         "io"
11         "log"
12 )
13
14 // debugTransport if set, will print packet types as they go over the
15 // wire. No message decoding is done, to minimize the impact on timing.
16 const debugTransport = false
17
18 const (
19         gcmCipherID    = "aes128-gcm@openssh.com"
20         aes128cbcID    = "aes128-cbc"
21         tripledescbcID = "3des-cbc"
22 )
23
24 // packetConn represents a transport that implements packet based
25 // operations.
26 type packetConn interface {
27         // Encrypt and send a packet of data to the remote peer.
28         writePacket(packet []byte) error
29
30         // Read a packet from the connection. The read is blocking,
31         // i.e. if error is nil, then the returned byte slice is
32         // always non-empty.
33         readPacket() ([]byte, error)
34
35         // Close closes the write-side of the connection.
36         Close() error
37 }
38
39 // transport is the keyingTransport that implements the SSH packet
40 // protocol.
41 type transport struct {
42         reader connectionState
43         writer connectionState
44
45         bufReader *bufio.Reader
46         bufWriter *bufio.Writer
47         rand      io.Reader
48         isClient  bool
49         io.Closer
50 }
51
52 // packetCipher represents a combination of SSH encryption/MAC
53 // protocol.  A single instance should be used for one direction only.
54 type packetCipher interface {
55         // writePacket encrypts the packet and writes it to w. The
56         // contents of the packet are generally scrambled.
57         writePacket(seqnum uint32, w io.Writer, rand io.Reader, packet []byte) error
58
59         // readPacket reads and decrypts a packet of data. The
60         // returned packet may be overwritten by future calls of
61         // readPacket.
62         readPacket(seqnum uint32, r io.Reader) ([]byte, error)
63 }
64
65 // connectionState represents one side (read or write) of the
66 // connection. This is necessary because each direction has its own
67 // keys, and can even have its own algorithms
68 type connectionState struct {
69         packetCipher
70         seqNum           uint32
71         dir              direction
72         pendingKeyChange chan packetCipher
73 }
74
75 // prepareKeyChange sets up key material for a keychange. The key changes in
76 // both directions are triggered by reading and writing a msgNewKey packet
77 // respectively.
78 func (t *transport) prepareKeyChange(algs *algorithms, kexResult *kexResult) error {
79         if ciph, err := newPacketCipher(t.reader.dir, algs.r, kexResult); err != nil {
80                 return err
81         } else {
82                 t.reader.pendingKeyChange <- ciph
83         }
84
85         if ciph, err := newPacketCipher(t.writer.dir, algs.w, kexResult); err != nil {
86                 return err
87         } else {
88                 t.writer.pendingKeyChange <- ciph
89         }
90
91         return nil
92 }
93
94 func (t *transport) printPacket(p []byte, write bool) {
95         if len(p) == 0 {
96                 return
97         }
98         who := "server"
99         if t.isClient {
100                 who = "client"
101         }
102         what := "read"
103         if write {
104                 what = "write"
105         }
106
107         log.Println(what, who, p[0])
108 }
109
110 // Read and decrypt next packet.
111 func (t *transport) readPacket() (p []byte, err error) {
112         for {
113                 p, err = t.reader.readPacket(t.bufReader)
114                 if err != nil {
115                         break
116                 }
117                 if len(p) == 0 || (p[0] != msgIgnore && p[0] != msgDebug) {
118                         break
119                 }
120         }
121         if debugTransport {
122                 t.printPacket(p, false)
123         }
124
125         return p, err
126 }
127
128 func (s *connectionState) readPacket(r *bufio.Reader) ([]byte, error) {
129         packet, err := s.packetCipher.readPacket(s.seqNum, r)
130         s.seqNum++
131         if err == nil && len(packet) == 0 {
132                 err = errors.New("ssh: zero length packet")
133         }
134
135         if len(packet) > 0 {
136                 switch packet[0] {
137                 case msgNewKeys:
138                         select {
139                         case cipher := <-s.pendingKeyChange:
140                                 s.packetCipher = cipher
141                         default:
142                                 return nil, errors.New("ssh: got bogus newkeys message.")
143                         }
144
145                 case msgDisconnect:
146                         // Transform a disconnect message into an
147                         // error. Since this is lowest level at which
148                         // we interpret message types, doing it here
149                         // ensures that we don't have to handle it
150                         // elsewhere.
151                         var msg disconnectMsg
152                         if err := Unmarshal(packet, &msg); err != nil {
153                                 return nil, err
154                         }
155                         return nil, &msg
156                 }
157         }
158
159         // The packet may point to an internal buffer, so copy the
160         // packet out here.
161         fresh := make([]byte, len(packet))
162         copy(fresh, packet)
163
164         return fresh, err
165 }
166
167 func (t *transport) writePacket(packet []byte) error {
168         if debugTransport {
169                 t.printPacket(packet, true)
170         }
171         return t.writer.writePacket(t.bufWriter, t.rand, packet)
172 }
173
174 func (s *connectionState) writePacket(w *bufio.Writer, rand io.Reader, packet []byte) error {
175         changeKeys := len(packet) > 0 && packet[0] == msgNewKeys
176
177         err := s.packetCipher.writePacket(s.seqNum, w, rand, packet)
178         if err != nil {
179                 return err
180         }
181         if err = w.Flush(); err != nil {
182                 return err
183         }
184         s.seqNum++
185         if changeKeys {
186                 select {
187                 case cipher := <-s.pendingKeyChange:
188                         s.packetCipher = cipher
189                 default:
190                         panic("ssh: no key material for msgNewKeys")
191                 }
192         }
193         return err
194 }
195
196 func newTransport(rwc io.ReadWriteCloser, rand io.Reader, isClient bool) *transport {
197         t := &transport{
198                 bufReader: bufio.NewReader(rwc),
199                 bufWriter: bufio.NewWriter(rwc),
200                 rand:      rand,
201                 reader: connectionState{
202                         packetCipher:     &streamPacketCipher{cipher: noneCipher{}},
203                         pendingKeyChange: make(chan packetCipher, 1),
204                 },
205                 writer: connectionState{
206                         packetCipher:     &streamPacketCipher{cipher: noneCipher{}},
207                         pendingKeyChange: make(chan packetCipher, 1),
208                 },
209                 Closer: rwc,
210         }
211         t.isClient = isClient
212
213         if isClient {
214                 t.reader.dir = serverKeys
215                 t.writer.dir = clientKeys
216         } else {
217                 t.reader.dir = clientKeys
218                 t.writer.dir = serverKeys
219         }
220
221         return t
222 }
223
224 type direction struct {
225         ivTag     []byte
226         keyTag    []byte
227         macKeyTag []byte
228 }
229
230 var (
231         serverKeys = direction{[]byte{'B'}, []byte{'D'}, []byte{'F'}}
232         clientKeys = direction{[]byte{'A'}, []byte{'C'}, []byte{'E'}}
233 )
234
235 // generateKeys generates key material for IV, MAC and encryption.
236 func generateKeys(d direction, algs directionAlgorithms, kex *kexResult) (iv, key, macKey []byte) {
237         cipherMode := cipherModes[algs.Cipher]
238         macMode := macModes[algs.MAC]
239
240         iv = make([]byte, cipherMode.ivSize)
241         key = make([]byte, cipherMode.keySize)
242         macKey = make([]byte, macMode.keySize)
243
244         generateKeyMaterial(iv, d.ivTag, kex)
245         generateKeyMaterial(key, d.keyTag, kex)
246         generateKeyMaterial(macKey, d.macKeyTag, kex)
247         return
248 }
249
250 // setupKeys sets the cipher and MAC keys from kex.K, kex.H and sessionId, as
251 // described in RFC 4253, section 6.4. direction should either be serverKeys
252 // (to setup server->client keys) or clientKeys (for client->server keys).
253 func newPacketCipher(d direction, algs directionAlgorithms, kex *kexResult) (packetCipher, error) {
254         iv, key, macKey := generateKeys(d, algs, kex)
255
256         if algs.Cipher == gcmCipherID {
257                 return newGCMCipher(iv, key, macKey)
258         }
259
260         if algs.Cipher == aes128cbcID {
261                 return newAESCBCCipher(iv, key, macKey, algs)
262         }
263
264         if algs.Cipher == tripledescbcID {
265                 return newTripleDESCBCCipher(iv, key, macKey, algs)
266         }
267
268         c := &streamPacketCipher{
269                 mac: macModes[algs.MAC].new(macKey),
270                 etm: macModes[algs.MAC].etm,
271         }
272         c.macResult = make([]byte, c.mac.Size())
273
274         var err error
275         c.cipher, err = cipherModes[algs.Cipher].createStream(key, iv)
276         if err != nil {
277                 return nil, err
278         }
279
280         return c, nil
281 }
282
283 // generateKeyMaterial fills out with key material generated from tag, K, H
284 // and sessionId, as specified in RFC 4253, section 7.2.
285 func generateKeyMaterial(out, tag []byte, r *kexResult) {
286         var digestsSoFar []byte
287
288         h := r.Hash.New()
289         for len(out) > 0 {
290                 h.Reset()
291                 h.Write(r.K)
292                 h.Write(r.H)
293
294                 if len(digestsSoFar) == 0 {
295                         h.Write(tag)
296                         h.Write(r.SessionID)
297                 } else {
298                         h.Write(digestsSoFar)
299                 }
300
301                 digest := h.Sum(nil)
302                 n := copy(out, digest)
303                 out = out[n:]
304                 if len(out) > 0 {
305                         digestsSoFar = append(digestsSoFar, digest...)
306                 }
307         }
308 }
309
310 const packageVersion = "SSH-2.0-Go"
311
312 // Sends and receives a version line.  The versionLine string should
313 // be US ASCII, start with "SSH-2.0-", and should not include a
314 // newline. exchangeVersions returns the other side's version line.
315 func exchangeVersions(rw io.ReadWriter, versionLine []byte) (them []byte, err error) {
316         // Contrary to the RFC, we do not ignore lines that don't
317         // start with "SSH-2.0-" to make the library usable with
318         // nonconforming servers.
319         for _, c := range versionLine {
320                 // The spec disallows non US-ASCII chars, and
321                 // specifically forbids null chars.
322                 if c < 32 {
323                         return nil, errors.New("ssh: junk character in version line")
324                 }
325         }
326         if _, err = rw.Write(append(versionLine, '\r', '\n')); err != nil {
327                 return
328         }
329
330         them, err = readVersion(rw)
331         return them, err
332 }
333
334 // maxVersionStringBytes is the maximum number of bytes that we'll
335 // accept as a version string. RFC 4253 section 4.2 limits this at 255
336 // chars
337 const maxVersionStringBytes = 255
338
339 // Read version string as specified by RFC 4253, section 4.2.
340 func readVersion(r io.Reader) ([]byte, error) {
341         versionString := make([]byte, 0, 64)
342         var ok bool
343         var buf [1]byte
344
345         for len(versionString) < maxVersionStringBytes {
346                 _, err := io.ReadFull(r, buf[:])
347                 if err != nil {
348                         return nil, err
349                 }
350                 // The RFC says that the version should be terminated with \r\n
351                 // but several SSH servers actually only send a \n.
352                 if buf[0] == '\n' {
353                         ok = true
354                         break
355                 }
356
357                 // non ASCII chars are disallowed, but we are lenient,
358                 // since Go doesn't use null-terminated strings.
359
360                 // The RFC allows a comment after a space, however,
361                 // all of it (version and comments) goes into the
362                 // session hash.
363                 versionString = append(versionString, buf[0])
364         }
365
366         if !ok {
367                 return nil, errors.New("ssh: overflow reading version string")
368         }
369
370         // There might be a '\r' on the end which we should remove.
371         if len(versionString) > 0 && versionString[len(versionString)-1] == '\r' {
372                 versionString = versionString[:len(versionString)-1]
373         }
374         return versionString, nil
375 }