OLD | NEW |
1 // Copyright 2011 The Go Authors. All rights reserved. | 1 // Copyright 2011 The Go Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style | 2 // Use of this source code is governed by a BSD-style |
3 // license that can be found in the LICENSE file. | 3 // license that can be found in the LICENSE file. |
4 | 4 |
5 package ssh | 5 package ssh |
6 | 6 |
7 import ( | 7 import ( |
8 "bytes" | 8 "bytes" |
9 "crypto" | 9 "crypto" |
10 "crypto/ecdsa" | 10 "crypto/ecdsa" |
11 "crypto/elliptic" | 11 "crypto/elliptic" |
12 "crypto/rand" | 12 "crypto/rand" |
13 "crypto/rsa" | |
14 "crypto/x509" | |
15 "encoding/binary" | 13 "encoding/binary" |
16 "encoding/pem" | |
17 "errors" | 14 "errors" |
18 "io" | 15 "io" |
19 "math/big" | 16 "math/big" |
20 "net" | 17 "net" |
21 "sync" | 18 "sync" |
22 | 19 |
23 _ "crypto/sha1" | 20 _ "crypto/sha1" |
24 ) | 21 ) |
25 | 22 |
26 type ServerConfig struct { | 23 type ServerConfig struct { |
27 » rsa *rsa.PrivateKey | 24 » hostKeys []Signer |
28 | |
29 » // rsaSerialized is the serialized form of the public key that | |
30 » // corresponds to the private key held in the rsa field. | |
31 » rsaSerialized []byte | |
32 | 25 |
33 // Rand provides the source of entropy for key exchange. If Rand is | 26 // Rand provides the source of entropy for key exchange. If Rand is |
34 // nil, the cryptographic random reader in package crypto/rand will | 27 // nil, the cryptographic random reader in package crypto/rand will |
35 // be used. | 28 // be used. |
36 Rand io.Reader | 29 Rand io.Reader |
37 | 30 |
38 // NoClientAuth is true if clients are allowed to connect without | 31 // NoClientAuth is true if clients are allowed to connect without |
39 // authenticating. | 32 // authenticating. |
40 NoClientAuth bool | 33 NoClientAuth bool |
41 | 34 |
(...skipping 20 matching lines...) Expand all Loading... |
62 Crypto CryptoConfig | 55 Crypto CryptoConfig |
63 } | 56 } |
64 | 57 |
65 func (c *ServerConfig) rand() io.Reader { | 58 func (c *ServerConfig) rand() io.Reader { |
66 if c.Rand == nil { | 59 if c.Rand == nil { |
67 return rand.Reader | 60 return rand.Reader |
68 } | 61 } |
69 return c.Rand | 62 return c.Rand |
70 } | 63 } |
71 | 64 |
| 65 // AddHostKey adds a private key as a host key. If an existing host |
| 66 // key exists with the same algorithm, it is overwritten. |
| 67 func (s *ServerConfig) AddHostKey(key Signer) { |
| 68 for i, k := range s.hostKeys { |
| 69 if k.PublicKey().PublicKeyAlgo() == key.PublicKey().PublicKeyAlg
o() { |
| 70 s.hostKeys[i] = key |
| 71 return |
| 72 } |
| 73 } |
| 74 |
| 75 s.hostKeys = append(s.hostKeys, key) |
| 76 } |
| 77 |
72 // SetRSAPrivateKey sets the private key for a Server. A Server must have a | 78 // SetRSAPrivateKey sets the private key for a Server. A Server must have a |
73 // private key configured in order to accept connections. The private key must | 79 // private key configured in order to accept connections. The private key must |
74 // be in the form of a PEM encoded, PKCS#1, RSA private key. The file "id_rsa" | 80 // be in the form of a PEM encoded, PKCS#1, RSA private key. The file "id_rsa" |
75 // typically contains such a key. | 81 // typically contains such a key. |
76 func (s *ServerConfig) SetRSAPrivateKey(pemBytes []byte) error { | 82 func (s *ServerConfig) SetRSAPrivateKey(pemBytes []byte) error { |
77 » block, _ := pem.Decode(pemBytes) | 83 » priv, err := ParsePrivateKey(pemBytes) |
78 » if block == nil { | |
79 » » return errors.New("ssh: no key found") | |
80 » } | |
81 » rsa, err := x509.ParsePKCS1PrivateKey(block.Bytes) | |
82 if err != nil { | 84 if err != nil { |
83 return err | 85 return err |
84 } | 86 } |
85 » s.rsa = rsa | 87 » s.AddHostKey(priv) |
86 » s.rsaSerialized = MarshalPublicKey(NewRSAPublicKey(&rsa.PublicKey)) | |
87 return nil | 88 return nil |
88 } | 89 } |
89 | 90 |
90 // cachedPubKey contains the results of querying whether a public key is | 91 // cachedPubKey contains the results of querying whether a public key is |
91 // acceptable for a user. The cache only applies to a single ServerConn. | 92 // acceptable for a user. The cache only applies to a single ServerConn. |
92 type cachedPubKey struct { | 93 type cachedPubKey struct { |
93 user, algo string | 94 user, algo string |
94 pubKey []byte | 95 pubKey []byte |
95 result bool | 96 result bool |
96 } | 97 } |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
134 func Server(c net.Conn, config *ServerConfig) *ServerConn { | 135 func Server(c net.Conn, config *ServerConfig) *ServerConn { |
135 return &ServerConn{ | 136 return &ServerConn{ |
136 transport: newTransport(c, config.rand()), | 137 transport: newTransport(c, config.rand()), |
137 channels: make(map[uint32]*serverChan), | 138 channels: make(map[uint32]*serverChan), |
138 config: config, | 139 config: config, |
139 } | 140 } |
140 } | 141 } |
141 | 142 |
142 // kexECDH performs Elliptic Curve Diffie-Hellman key agreement on a | 143 // kexECDH performs Elliptic Curve Diffie-Hellman key agreement on a |
143 // ServerConnection, as documented in RFC 5656, section 4. | 144 // ServerConnection, as documented in RFC 5656, section 4. |
144 func (s *ServerConn) kexECDH(curve elliptic.Curve, magics *handshakeMagics, host
KeyAlgo string) (result *kexResult, err error) { | 145 func (s *ServerConn) kexECDH(curve elliptic.Curve, magics *handshakeMagics, priv
Signer) (result *kexResult, err error) { |
145 packet, err := s.readPacket() | 146 packet, err := s.readPacket() |
146 if err != nil { | 147 if err != nil { |
147 return | 148 return |
148 } | 149 } |
149 | 150 |
150 var kexECDHInit kexECDHInitMsg | 151 var kexECDHInit kexECDHInitMsg |
151 if err = unmarshal(&kexECDHInit, packet, msgKexECDHInit); err != nil { | 152 if err = unmarshal(&kexECDHInit, packet, msgKexECDHInit); err != nil { |
152 return | 153 return |
153 } | 154 } |
154 | 155 |
155 clientX, clientY := elliptic.Unmarshal(curve, kexECDHInit.ClientPubKey) | 156 clientX, clientY := elliptic.Unmarshal(curve, kexECDHInit.ClientPubKey) |
156 if clientX == nil { | 157 if clientX == nil { |
157 return nil, errors.New("ssh: elliptic.Unmarshal failure") | 158 return nil, errors.New("ssh: elliptic.Unmarshal failure") |
158 } | 159 } |
159 | 160 |
160 if !validateECPublicKey(curve, clientX, clientY) { | 161 if !validateECPublicKey(curve, clientX, clientY) { |
161 return nil, errors.New("ssh: not a valid EC public key") | 162 return nil, errors.New("ssh: not a valid EC public key") |
162 } | 163 } |
163 | 164 |
164 // We could cache this key across multiple users/multiple | 165 // We could cache this key across multiple users/multiple |
165 // connection attempts, but the benefit is small. OpenSSH | 166 // connection attempts, but the benefit is small. OpenSSH |
166 // generates a new key for each incoming connection. | 167 // generates a new key for each incoming connection. |
167 ephKey, err := ecdsa.GenerateKey(curve, s.config.rand()) | 168 ephKey, err := ecdsa.GenerateKey(curve, s.config.rand()) |
168 if err != nil { | 169 if err != nil { |
169 return nil, err | 170 return nil, err |
170 } | 171 } |
171 | 172 |
172 » hostKeyBytes := s.config.rsaSerialized | 173 » hostKeyBytes := MarshalPublicKey(priv.PublicKey()) |
173 | 174 |
174 serializedEphKey := elliptic.Marshal(curve, ephKey.PublicKey.X, ephKey.P
ublicKey.Y) | 175 serializedEphKey := elliptic.Marshal(curve, ephKey.PublicKey.X, ephKey.P
ublicKey.Y) |
175 | 176 |
176 // generate shared secret | 177 // generate shared secret |
177 secret, _ := curve.ScalarMult(clientX, clientY, ephKey.D.Bytes()) | 178 secret, _ := curve.ScalarMult(clientX, clientY, ephKey.D.Bytes()) |
178 | 179 |
179 hashFunc := ecHash(curve) | 180 hashFunc := ecHash(curve) |
180 h := hashFunc.New() | 181 h := hashFunc.New() |
181 writeString(h, magics.clientVersion) | 182 writeString(h, magics.clientVersion) |
182 writeString(h, magics.serverVersion) | 183 writeString(h, magics.serverVersion) |
183 writeString(h, magics.clientKexInit) | 184 writeString(h, magics.clientKexInit) |
184 writeString(h, magics.serverKexInit) | 185 writeString(h, magics.serverKexInit) |
185 writeString(h, hostKeyBytes) | 186 writeString(h, hostKeyBytes) |
186 writeString(h, kexECDHInit.ClientPubKey) | 187 writeString(h, kexECDHInit.ClientPubKey) |
187 writeString(h, serializedEphKey) | 188 writeString(h, serializedEphKey) |
188 | 189 |
189 K := make([]byte, intLength(secret)) | 190 K := make([]byte, intLength(secret)) |
190 marshalInt(K, secret) | 191 marshalInt(K, secret) |
191 h.Write(K) | 192 h.Write(K) |
192 | 193 |
193 H := h.Sum(nil) | 194 H := h.Sum(nil) |
194 | 195 |
195 » sig, err := s.serializedHostKeySignature(hostKeyAlgo, H) | 196 » // H is already a hash, but the hostkey signing will apply its |
| 197 » // own key specific hash algorithm. |
| 198 » sig, err := signAndMarshal(priv, s.config.rand(), H) |
196 if err != nil { | 199 if err != nil { |
197 return nil, err | 200 return nil, err |
198 } | 201 } |
199 | 202 |
200 reply := kexECDHReplyMsg{ | 203 reply := kexECDHReplyMsg{ |
201 EphemeralPubKey: serializedEphKey, | 204 EphemeralPubKey: serializedEphKey, |
202 HostKey: hostKeyBytes, | 205 HostKey: hostKeyBytes, |
203 Signature: sig, | 206 Signature: sig, |
204 } | 207 } |
205 | 208 |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
240 // - the NIST curves have cofactor = 1, so this is implicit. | 243 // - the NIST curves have cofactor = 1, so this is implicit. |
241 // (We don't forsee an implementation that supports non NIST | 244 // (We don't forsee an implementation that supports non NIST |
242 // curves) | 245 // curves) |
243 // | 246 // |
244 // - for ephemeral keys, we don't need to worry about small | 247 // - for ephemeral keys, we don't need to worry about small |
245 // subgroup attacks. | 248 // subgroup attacks. |
246 return true | 249 return true |
247 } | 250 } |
248 | 251 |
249 // kexDH performs Diffie-Hellman key agreement on a ServerConnection. | 252 // kexDH performs Diffie-Hellman key agreement on a ServerConnection. |
250 func (s *ServerConn) kexDH(group *dhGroup, hashFunc crypto.Hash, magics *handsha
keMagics, hostKeyAlgo string) (result *kexResult, err error) { | 253 func (s *ServerConn) kexDH(group *dhGroup, hashFunc crypto.Hash, magics *handsha
keMagics, priv Signer) (result *kexResult, err error) { |
251 packet, err := s.readPacket() | 254 packet, err := s.readPacket() |
252 if err != nil { | 255 if err != nil { |
253 return | 256 return |
254 } | 257 } |
255 var kexDHInit kexDHInitMsg | 258 var kexDHInit kexDHInitMsg |
256 if err = unmarshal(&kexDHInit, packet, msgKexDHInit); err != nil { | 259 if err = unmarshal(&kexDHInit, packet, msgKexDHInit); err != nil { |
257 return | 260 return |
258 } | 261 } |
259 | 262 |
260 y, err := rand.Int(s.config.rand(), group.p) | 263 y, err := rand.Int(s.config.rand(), group.p) |
261 if err != nil { | 264 if err != nil { |
262 return | 265 return |
263 } | 266 } |
264 | 267 |
265 Y := new(big.Int).Exp(group.g, y, group.p) | 268 Y := new(big.Int).Exp(group.g, y, group.p) |
266 kInt, err := group.diffieHellman(kexDHInit.X, y) | 269 kInt, err := group.diffieHellman(kexDHInit.X, y) |
267 if err != nil { | 270 if err != nil { |
268 return nil, err | 271 return nil, err |
269 } | 272 } |
270 | 273 |
271 » hostKeyBytes := s.config.rsaSerialized | 274 » hostKeyBytes := MarshalPublicKey(priv.PublicKey()) |
272 | 275 |
273 h := hashFunc.New() | 276 h := hashFunc.New() |
274 writeString(h, magics.clientVersion) | 277 writeString(h, magics.clientVersion) |
275 writeString(h, magics.serverVersion) | 278 writeString(h, magics.serverVersion) |
276 writeString(h, magics.clientKexInit) | 279 writeString(h, magics.clientKexInit) |
277 writeString(h, magics.serverKexInit) | 280 writeString(h, magics.serverKexInit) |
278 writeString(h, hostKeyBytes) | 281 writeString(h, hostKeyBytes) |
279 writeInt(h, kexDHInit.X) | 282 writeInt(h, kexDHInit.X) |
280 writeInt(h, Y) | 283 writeInt(h, Y) |
281 | 284 |
282 K := make([]byte, intLength(kInt)) | 285 K := make([]byte, intLength(kInt)) |
283 marshalInt(K, kInt) | 286 marshalInt(K, kInt) |
284 h.Write(K) | 287 h.Write(K) |
285 | 288 |
286 H := h.Sum(nil) | 289 H := h.Sum(nil) |
287 | 290 |
288 » sig, err := s.serializedHostKeySignature(hostKeyAlgo, H) | 291 » // H is already a hash, but the hostkey signing will apply its |
| 292 » // own key specific hash algorithm. |
| 293 » sig, err := signAndMarshal(priv, s.config.rand(), H) |
289 if err != nil { | 294 if err != nil { |
290 return nil, err | 295 return nil, err |
291 } | 296 } |
292 | 297 |
293 kexDHReply := kexDHReplyMsg{ | 298 kexDHReply := kexDHReplyMsg{ |
294 HostKey: hostKeyBytes, | 299 HostKey: hostKeyBytes, |
295 Y: Y, | 300 Y: Y, |
296 Signature: sig, | 301 Signature: sig, |
297 } | 302 } |
298 packet = marshal(msgKexDHReply, kexDHReply) | 303 packet = marshal(msgKexDHReply, kexDHReply) |
299 | 304 |
300 err = s.writePacket(packet) | 305 err = s.writePacket(packet) |
301 return &kexResult{ | 306 return &kexResult{ |
302 H: H, | 307 H: H, |
303 K: K, | 308 K: K, |
304 HostKey: hostKeyBytes, | 309 HostKey: hostKeyBytes, |
305 Hash: hashFunc, | 310 Hash: hashFunc, |
306 }, nil | 311 }, nil |
307 } | 312 } |
308 | 313 |
309 // serializedHostKeySignature signs the hashed data, and serializes | 314 // signAndMarshal signs the data with the appropriate algorithm, |
310 // the signature according to SSH conventions. | 315 // and serializes the result in SSH wire format. |
311 func (s *ServerConn) serializedHostKeySignature(hostKeyAlgo string, hashed []byt
e) ([]byte, error) { | 316 func signAndMarshal(k Signer, rand io.Reader, data []byte) ([]byte, error) { |
312 » var sig []byte | 317 » sig, err := k.Sign(rand, data) |
313 » switch hostKeyAlgo { | 318 » if err != nil { |
314 » case hostAlgoRSA: | 319 » » return nil, err |
315 » » hashFunc := crypto.SHA1 | |
316 » » hh := hashFunc.New() | |
317 » » hh.Write(hashed) | |
318 » » var err error | |
319 » » sig, err = rsa.SignPKCS1v15(s.config.rand(), s.config.rsa, hashF
unc, hh.Sum(nil)) | |
320 » » if err != nil { | |
321 » » » return nil, err | |
322 » » } | |
323 » default: | |
324 » » return nil, errors.New("ssh: internal error") | |
325 } | 320 } |
326 | 321 |
327 » return serializeSignature(hostKeyAlgo, sig), nil | 322 » return serializeSignature(k.PublicKey().PrivateKeyAlgo(), sig), nil |
328 } | 323 } |
329 | 324 |
330 // serverVersion is the fixed identification string that Server will use. | 325 // serverVersion is the fixed identification string that Server will use. |
331 var serverVersion = []byte("SSH-2.0-Go\r\n") | 326 var serverVersion = []byte("SSH-2.0-Go\r\n") |
332 | 327 |
333 // Handshake performs an SSH transport and client authentication on the given Se
rverConn. | 328 // Handshake performs an SSH transport and client authentication on the given Se
rverConn. |
334 func (s *ServerConn) Handshake() (err error) { | 329 func (s *ServerConn) Handshake() (err error) { |
335 if _, err = s.Write(serverVersion); err != nil { | 330 if _, err = s.Write(serverVersion); err != nil { |
336 return | 331 return |
337 } | 332 } |
(...skipping 29 matching lines...) Expand all Loading... |
367 | 362 |
368 if err = s.authenticate(s.sessionId); err != nil { | 363 if err = s.authenticate(s.sessionId); err != nil { |
369 return | 364 return |
370 } | 365 } |
371 return | 366 return |
372 } | 367 } |
373 | 368 |
374 func (s *ServerConn) clientInitHandshake(clientKexInit *kexInitMsg, clientKexIni
tPacket []byte) (err error) { | 369 func (s *ServerConn) clientInitHandshake(clientKexInit *kexInitMsg, clientKexIni
tPacket []byte) (err error) { |
375 serverKexInit := kexInitMsg{ | 370 serverKexInit := kexInitMsg{ |
376 KexAlgos: s.config.Crypto.kexes(), | 371 KexAlgos: s.config.Crypto.kexes(), |
377 ServerHostKeyAlgos: supportedHostKeyAlgos, | |
378 CiphersClientServer: s.config.Crypto.ciphers(), | 372 CiphersClientServer: s.config.Crypto.ciphers(), |
379 CiphersServerClient: s.config.Crypto.ciphers(), | 373 CiphersServerClient: s.config.Crypto.ciphers(), |
380 MACsClientServer: s.config.Crypto.macs(), | 374 MACsClientServer: s.config.Crypto.macs(), |
381 MACsServerClient: s.config.Crypto.macs(), | 375 MACsServerClient: s.config.Crypto.macs(), |
382 CompressionClientServer: supportedCompressions, | 376 CompressionClientServer: supportedCompressions, |
383 CompressionServerClient: supportedCompressions, | 377 CompressionServerClient: supportedCompressions, |
384 } | 378 } |
| 379 for _, k := range s.config.hostKeys { |
| 380 serverKexInit.ServerHostKeyAlgos = append( |
| 381 serverKexInit.ServerHostKeyAlgos, k.PublicKey().PublicKe
yAlgo()) |
| 382 } |
| 383 |
385 serverKexInitPacket := marshal(msgKexInit, serverKexInit) | 384 serverKexInitPacket := marshal(msgKexInit, serverKexInit) |
386 | |
387 if err = s.writePacket(serverKexInitPacket); err != nil { | 385 if err = s.writePacket(serverKexInitPacket); err != nil { |
388 return | 386 return |
389 } | 387 } |
390 | 388 |
391 if clientKexInitPacket == nil { | 389 if clientKexInitPacket == nil { |
392 clientKexInit = new(kexInitMsg) | 390 clientKexInit = new(kexInitMsg) |
393 if clientKexInitPacket, err = s.readPacket(); err != nil { | 391 if clientKexInitPacket, err = s.readPacket(); err != nil { |
394 return | 392 return |
395 } | 393 } |
396 if err = unmarshal(clientKexInit, clientKexInitPacket, msgKexIni
t); err != nil { | 394 if err = unmarshal(clientKexInit, clientKexInitPacket, msgKexIni
t); err != nil { |
397 return | 395 return |
398 } | 396 } |
399 } | 397 } |
400 | 398 |
401 kexAlgo, hostKeyAlgo, ok := findAgreedAlgorithms(s.transport, clientKexI
nit, &serverKexInit) | 399 kexAlgo, hostKeyAlgo, ok := findAgreedAlgorithms(s.transport, clientKexI
nit, &serverKexInit) |
402 if !ok { | 400 if !ok { |
403 return errors.New("ssh: no common algorithms") | 401 return errors.New("ssh: no common algorithms") |
404 } | 402 } |
405 if clientKexInit.FirstKexFollows && kexAlgo != clientKexInit.KexAlgos[0]
{ | 403 if clientKexInit.FirstKexFollows && kexAlgo != clientKexInit.KexAlgos[0]
{ |
406 // The client sent a Kex message for the wrong algorithm, | 404 // The client sent a Kex message for the wrong algorithm, |
407 // which we have to ignore. | 405 // which we have to ignore. |
408 if _, err = s.readPacket(); err != nil { | 406 if _, err = s.readPacket(); err != nil { |
409 return | 407 return |
410 } | 408 } |
411 } | 409 } |
412 | 410 |
| 411 var hostKey Signer |
| 412 for _, k := range s.config.hostKeys { |
| 413 if hostKeyAlgo == k.PublicKey().PublicKeyAlgo() { |
| 414 hostKey = k |
| 415 } |
| 416 } |
| 417 |
413 var magics handshakeMagics | 418 var magics handshakeMagics |
414 magics.serverVersion = serverVersion[:len(serverVersion)-2] | 419 magics.serverVersion = serverVersion[:len(serverVersion)-2] |
415 magics.clientVersion = s.ClientVersion | 420 magics.clientVersion = s.ClientVersion |
416 magics.serverKexInit = marshal(msgKexInit, serverKexInit) | 421 magics.serverKexInit = marshal(msgKexInit, serverKexInit) |
417 magics.clientKexInit = clientKexInitPacket | 422 magics.clientKexInit = clientKexInitPacket |
418 | 423 |
419 var result *kexResult | 424 var result *kexResult |
420 switch kexAlgo { | 425 switch kexAlgo { |
421 case kexAlgoECDH256: | 426 case kexAlgoECDH256: |
422 » » result, err = s.kexECDH(elliptic.P256(), &magics, hostKeyAlgo) | 427 » » result, err = s.kexECDH(elliptic.P256(), &magics, hostKey) |
423 case kexAlgoECDH384: | 428 case kexAlgoECDH384: |
424 » » result, err = s.kexECDH(elliptic.P384(), &magics, hostKeyAlgo) | 429 » » result, err = s.kexECDH(elliptic.P384(), &magics, hostKey) |
425 case kexAlgoECDH521: | 430 case kexAlgoECDH521: |
426 » » result, err = s.kexECDH(elliptic.P521(), &magics, hostKeyAlgo) | 431 » » result, err = s.kexECDH(elliptic.P521(), &magics, hostKey) |
427 case kexAlgoDH14SHA1: | 432 case kexAlgoDH14SHA1: |
428 dhGroup14Once.Do(initDHGroup14) | 433 dhGroup14Once.Do(initDHGroup14) |
429 » » result, err = s.kexDH(dhGroup14, crypto.SHA1, &magics, hostKeyAl
go) | 434 » » result, err = s.kexDH(dhGroup14, crypto.SHA1, &magics, hostKey) |
430 case kexAlgoDH1SHA1: | 435 case kexAlgoDH1SHA1: |
431 dhGroup1Once.Do(initDHGroup1) | 436 dhGroup1Once.Do(initDHGroup1) |
432 » » result, err = s.kexDH(dhGroup1, crypto.SHA1, &magics, hostKeyAlg
o) | 437 » » result, err = s.kexDH(dhGroup1, crypto.SHA1, &magics, hostKey) |
433 default: | 438 default: |
434 err = errors.New("ssh: unexpected key exchange algorithm " + kex
Algo) | 439 err = errors.New("ssh: unexpected key exchange algorithm " + kex
Algo) |
435 } | 440 } |
436 if err != nil { | 441 if err != nil { |
437 return | 442 return |
438 } | 443 } |
439 // sessionId must only be assigned during initial handshake. | 444 // sessionId must only be assigned during initial handshake. |
440 if s.sessionId == nil { | 445 if s.sessionId == nil { |
441 s.sessionId = result.H | 446 s.sessionId = result.H |
442 } | 447 } |
(...skipping 429 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
872 func Listen(network, addr string, config *ServerConfig) (*Listener, error) { | 877 func Listen(network, addr string, config *ServerConfig) (*Listener, error) { |
873 l, err := net.Listen(network, addr) | 878 l, err := net.Listen(network, addr) |
874 if err != nil { | 879 if err != nil { |
875 return nil, err | 880 return nil, err |
876 } | 881 } |
877 return &Listener{ | 882 return &Listener{ |
878 l, | 883 l, |
879 config, | 884 config, |
880 }, nil | 885 }, nil |
881 } | 886 } |
OLD | NEW |