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