github.com/glycerine/xcryptossh@v7.0.4+incompatible/kex.go (about) 1 // Copyright 2013 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 "context" 9 "crypto" 10 "crypto/ecdsa" 11 "crypto/elliptic" 12 "crypto/rand" 13 "crypto/subtle" 14 "errors" 15 "io" 16 "math/big" 17 18 "golang.org/x/crypto/curve25519" 19 ) 20 21 const ( 22 kexAlgoDH1SHA1 = "diffie-hellman-group1-sha1" 23 kexAlgoDH14SHA1 = "diffie-hellman-group14-sha1" 24 kexAlgoECDH256 = "ecdh-sha2-nistp256" 25 kexAlgoECDH384 = "ecdh-sha2-nistp384" 26 kexAlgoECDH521 = "ecdh-sha2-nistp521" 27 kexAlgoCurve25519SHA256 = "curve25519-sha256@libssh.org" 28 ) 29 30 // kexResult captures the outcome of a key exchange. 31 type kexResult struct { 32 // Session hash. See also RFC 4253, section 8. 33 H []byte 34 35 // Shared secret. See also RFC 4253, section 8. 36 K []byte 37 38 // Host key as hashed into H. 39 HostKey []byte 40 41 // Signature of H. 42 Signature []byte 43 44 // A cryptographic hash function that matches the security 45 // level of the key exchange algorithm. It is used for 46 // calculating H, and for deriving keys from H and K. 47 Hash crypto.Hash 48 49 // The session ID, which is the first H computed. This is used 50 // to derive key material inside the transport. 51 SessionID []byte 52 } 53 54 // handshakeMagics contains data that is always included in the 55 // session hash. 56 type handshakeMagics struct { 57 clientVersion, serverVersion []byte 58 clientKexInit, serverKexInit []byte 59 } 60 61 func (m *handshakeMagics) write(w io.Writer) { 62 writeString(w, m.clientVersion) 63 writeString(w, m.serverVersion) 64 writeString(w, m.clientKexInit) 65 writeString(w, m.serverKexInit) 66 } 67 68 // kexAlgorithm abstracts different key exchange algorithms. 69 type kexAlgorithm interface { 70 // Server runs server-side key agreement, signing the result 71 // with a hostkey. 72 Server(ctx context.Context, p packetConn, rand io.Reader, magics *handshakeMagics, s Signer) (*kexResult, error) 73 74 // Client runs the client-side key agreement. Caller is 75 // responsible for verifying the host key signature. 76 Client(ctx context.Context, p packetConn, rand io.Reader, magics *handshakeMagics) (*kexResult, error) 77 } 78 79 // dhGroup is a multiplicative group suitable for implementing Diffie-Hellman key agreement. 80 type dhGroup struct { 81 g, p, pMinus1 *big.Int 82 } 83 84 func (group *dhGroup) diffieHellman(theirPublic, myPrivate *big.Int) (*big.Int, error) { 85 if theirPublic.Cmp(bigOne) <= 0 || theirPublic.Cmp(group.pMinus1) >= 0 { 86 return nil, errors.New("ssh: DH parameter out of bounds") 87 } 88 return new(big.Int).Exp(theirPublic, myPrivate, group.p), nil 89 } 90 91 func (group *dhGroup) Client(ctx context.Context, c packetConn, randSource io.Reader, magics *handshakeMagics) (*kexResult, error) { 92 hashFunc := crypto.SHA1 93 94 var x *big.Int 95 for { 96 var err error 97 if x, err = rand.Int(randSource, group.pMinus1); err != nil { 98 return nil, err 99 } 100 if x.Sign() > 0 { 101 break 102 } 103 } 104 105 X := new(big.Int).Exp(group.g, x, group.p) 106 kexDHInit := kexDHInitMsg{ 107 X: X, 108 } 109 if err := c.writePacket(Marshal(&kexDHInit)); err != nil { 110 return nil, err 111 } 112 113 packet, err := c.readPacket(ctx) 114 if err != nil { 115 return nil, err 116 } 117 118 var kexDHReply kexDHReplyMsg 119 if err = Unmarshal(packet, &kexDHReply); err != nil { 120 return nil, err 121 } 122 123 kInt, err := group.diffieHellman(kexDHReply.Y, x) 124 if err != nil { 125 return nil, err 126 } 127 128 h := hashFunc.New() 129 magics.write(h) 130 writeString(h, kexDHReply.HostKey) 131 writeInt(h, X) 132 writeInt(h, kexDHReply.Y) 133 K := make([]byte, intLength(kInt)) 134 marshalInt(K, kInt) 135 h.Write(K) 136 137 return &kexResult{ 138 H: h.Sum(nil), 139 K: K, 140 HostKey: kexDHReply.HostKey, 141 Signature: kexDHReply.Signature, 142 Hash: crypto.SHA1, 143 }, nil 144 } 145 146 func (group *dhGroup) Server(ctx context.Context, c packetConn, randSource io.Reader, magics *handshakeMagics, priv Signer) (result *kexResult, err error) { 147 hashFunc := crypto.SHA1 148 packet, err := c.readPacket(ctx) 149 if err != nil { 150 return 151 } 152 var kexDHInit kexDHInitMsg 153 if err = Unmarshal(packet, &kexDHInit); err != nil { 154 return 155 } 156 157 var y *big.Int 158 for { 159 if y, err = rand.Int(randSource, group.pMinus1); err != nil { 160 return 161 } 162 if y.Sign() > 0 { 163 break 164 } 165 } 166 167 Y := new(big.Int).Exp(group.g, y, group.p) 168 kInt, err := group.diffieHellman(kexDHInit.X, y) 169 if err != nil { 170 return nil, err 171 } 172 173 hostKeyBytes := priv.PublicKey().Marshal() 174 175 h := hashFunc.New() 176 magics.write(h) 177 writeString(h, hostKeyBytes) 178 writeInt(h, kexDHInit.X) 179 writeInt(h, Y) 180 181 K := make([]byte, intLength(kInt)) 182 marshalInt(K, kInt) 183 h.Write(K) 184 185 H := h.Sum(nil) 186 187 // H is already a hash, but the hostkey signing will apply its 188 // own key-specific hash algorithm. 189 sig, err := signAndMarshal(priv, randSource, H) 190 if err != nil { 191 return nil, err 192 } 193 194 kexDHReply := kexDHReplyMsg{ 195 HostKey: hostKeyBytes, 196 Y: Y, 197 Signature: sig, 198 } 199 packet = Marshal(&kexDHReply) 200 201 err = c.writePacket(packet) 202 return &kexResult{ 203 H: H, 204 K: K, 205 HostKey: hostKeyBytes, 206 Signature: sig, 207 Hash: crypto.SHA1, 208 }, nil 209 } 210 211 // ecdh performs Elliptic Curve Diffie-Hellman key exchange as 212 // described in RFC 5656, section 4. 213 type ecdh struct { 214 curve elliptic.Curve 215 } 216 217 func (kex *ecdh) Client(ctx context.Context, c packetConn, rand io.Reader, magics *handshakeMagics) (*kexResult, error) { 218 ephKey, err := ecdsa.GenerateKey(kex.curve, rand) 219 if err != nil { 220 return nil, err 221 } 222 223 kexInit := kexECDHInitMsg{ 224 ClientPubKey: elliptic.Marshal(kex.curve, ephKey.PublicKey.X, ephKey.PublicKey.Y), 225 } 226 227 serialized := Marshal(&kexInit) 228 if err := c.writePacket(serialized); err != nil { 229 return nil, err 230 } 231 232 packet, err := c.readPacket(ctx) 233 if err != nil { 234 return nil, err 235 } 236 237 var reply kexECDHReplyMsg 238 if err = Unmarshal(packet, &reply); err != nil { 239 return nil, err 240 } 241 242 x, y, err := unmarshalECKey(kex.curve, reply.EphemeralPubKey) 243 if err != nil { 244 return nil, err 245 } 246 247 // generate shared secret 248 secret, _ := kex.curve.ScalarMult(x, y, ephKey.D.Bytes()) 249 250 h := ecHash(kex.curve).New() 251 magics.write(h) 252 writeString(h, reply.HostKey) 253 writeString(h, kexInit.ClientPubKey) 254 writeString(h, reply.EphemeralPubKey) 255 K := make([]byte, intLength(secret)) 256 marshalInt(K, secret) 257 h.Write(K) 258 259 return &kexResult{ 260 H: h.Sum(nil), 261 K: K, 262 HostKey: reply.HostKey, 263 Signature: reply.Signature, 264 Hash: ecHash(kex.curve), 265 }, nil 266 } 267 268 // unmarshalECKey parses and checks an EC key. 269 func unmarshalECKey(curve elliptic.Curve, pubkey []byte) (x, y *big.Int, err error) { 270 x, y = elliptic.Unmarshal(curve, pubkey) 271 if x == nil { 272 return nil, nil, errors.New("ssh: elliptic.Unmarshal failure") 273 } 274 if !validateECPublicKey(curve, x, y) { 275 return nil, nil, errors.New("ssh: public key not on curve") 276 } 277 return x, y, nil 278 } 279 280 // validateECPublicKey checks that the point is a valid public key for 281 // the given curve. See [SEC1], 3.2.2 282 func validateECPublicKey(curve elliptic.Curve, x, y *big.Int) bool { 283 if x.Sign() == 0 && y.Sign() == 0 { 284 return false 285 } 286 287 if x.Cmp(curve.Params().P) >= 0 { 288 return false 289 } 290 291 if y.Cmp(curve.Params().P) >= 0 { 292 return false 293 } 294 295 if !curve.IsOnCurve(x, y) { 296 return false 297 } 298 299 // We don't check if N * PubKey == 0, since 300 // 301 // - the NIST curves have cofactor = 1, so this is implicit. 302 // (We don't foresee an implementation that supports non NIST 303 // curves) 304 // 305 // - for ephemeral keys, we don't need to worry about small 306 // subgroup attacks. 307 return true 308 } 309 310 func (kex *ecdh) Server(ctx context.Context, c packetConn, rand io.Reader, magics *handshakeMagics, priv Signer) (result *kexResult, err error) { 311 packet, err := c.readPacket(ctx) 312 if err != nil { 313 return nil, err 314 } 315 316 var kexECDHInit kexECDHInitMsg 317 if err = Unmarshal(packet, &kexECDHInit); err != nil { 318 return nil, err 319 } 320 321 clientX, clientY, err := unmarshalECKey(kex.curve, kexECDHInit.ClientPubKey) 322 if err != nil { 323 return nil, err 324 } 325 326 // We could cache this key across multiple users/multiple 327 // connection attempts, but the benefit is small. OpenSSH 328 // generates a new key for each incoming connection. 329 ephKey, err := ecdsa.GenerateKey(kex.curve, rand) 330 if err != nil { 331 return nil, err 332 } 333 334 hostKeyBytes := priv.PublicKey().Marshal() 335 336 serializedEphKey := elliptic.Marshal(kex.curve, ephKey.PublicKey.X, ephKey.PublicKey.Y) 337 338 // generate shared secret 339 secret, _ := kex.curve.ScalarMult(clientX, clientY, ephKey.D.Bytes()) 340 341 h := ecHash(kex.curve).New() 342 magics.write(h) 343 writeString(h, hostKeyBytes) 344 writeString(h, kexECDHInit.ClientPubKey) 345 writeString(h, serializedEphKey) 346 347 K := make([]byte, intLength(secret)) 348 marshalInt(K, secret) 349 h.Write(K) 350 351 H := h.Sum(nil) 352 353 // H is already a hash, but the hostkey signing will apply its 354 // own key-specific hash algorithm. 355 sig, err := signAndMarshal(priv, rand, H) 356 if err != nil { 357 return nil, err 358 } 359 360 reply := kexECDHReplyMsg{ 361 EphemeralPubKey: serializedEphKey, 362 HostKey: hostKeyBytes, 363 Signature: sig, 364 } 365 366 serialized := Marshal(&reply) 367 if err := c.writePacket(serialized); err != nil { 368 return nil, err 369 } 370 371 return &kexResult{ 372 H: H, 373 K: K, 374 HostKey: reply.HostKey, 375 Signature: sig, 376 Hash: ecHash(kex.curve), 377 }, nil 378 } 379 380 var kexAlgoMap = map[string]kexAlgorithm{} 381 382 func init() { 383 // This is the group called diffie-hellman-group1-sha1 in RFC 384 // 4253 and Oakley Group 2 in RFC 2409. 385 p, _ := new(big.Int).SetString("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF", 16) 386 kexAlgoMap[kexAlgoDH1SHA1] = &dhGroup{ 387 g: new(big.Int).SetInt64(2), 388 p: p, 389 pMinus1: new(big.Int).Sub(p, bigOne), 390 } 391 392 // This is the group called diffie-hellman-group14-sha1 in RFC 393 // 4253 and Oakley Group 14 in RFC 3526. 394 p, _ = new(big.Int).SetString("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF", 16) 395 396 kexAlgoMap[kexAlgoDH14SHA1] = &dhGroup{ 397 g: new(big.Int).SetInt64(2), 398 p: p, 399 pMinus1: new(big.Int).Sub(p, bigOne), 400 } 401 402 kexAlgoMap[kexAlgoECDH521] = &ecdh{elliptic.P521()} 403 kexAlgoMap[kexAlgoECDH384] = &ecdh{elliptic.P384()} 404 kexAlgoMap[kexAlgoECDH256] = &ecdh{elliptic.P256()} 405 kexAlgoMap[kexAlgoCurve25519SHA256] = &curve25519sha256{} 406 } 407 408 // curve25519sha256 implements the curve25519-sha256@libssh.org key 409 // agreement protocol, as described in 410 // https://git.libssh.org/projects/libssh.git/tree/doc/curve25519-sha256@libssh.org.txt 411 type curve25519sha256 struct{} 412 413 type curve25519KeyPair struct { 414 priv [32]byte 415 pub [32]byte 416 } 417 418 func (kp *curve25519KeyPair) generate(rand io.Reader) error { 419 if _, err := io.ReadFull(rand, kp.priv[:]); err != nil { 420 return err 421 } 422 curve25519.ScalarBaseMult(&kp.pub, &kp.priv) 423 return nil 424 } 425 426 // curve25519Zeros is just an array of 32 zero bytes so that we have something 427 // convenient to compare against in order to reject curve25519 points with the 428 // wrong order. 429 var curve25519Zeros [32]byte 430 431 func (kex *curve25519sha256) Client(ctx context.Context, c packetConn, rand io.Reader, magics *handshakeMagics) (*kexResult, error) { 432 var kp curve25519KeyPair 433 if err := kp.generate(rand); err != nil { 434 return nil, err 435 } 436 if err := c.writePacket(Marshal(&kexECDHInitMsg{kp.pub[:]})); err != nil { 437 return nil, err 438 } 439 440 packet, err := c.readPacket(ctx) 441 if err != nil { 442 return nil, err 443 } 444 445 var reply kexECDHReplyMsg 446 if err = Unmarshal(packet, &reply); err != nil { 447 return nil, err 448 } 449 if len(reply.EphemeralPubKey) != 32 { 450 return nil, errors.New("ssh: peer's curve25519 public value has wrong length") 451 } 452 453 var servPub, secret [32]byte 454 copy(servPub[:], reply.EphemeralPubKey) 455 curve25519.ScalarMult(&secret, &kp.priv, &servPub) 456 if subtle.ConstantTimeCompare(secret[:], curve25519Zeros[:]) == 1 { 457 return nil, errors.New("ssh: peer's curve25519 public value has wrong order") 458 } 459 460 h := crypto.SHA256.New() 461 magics.write(h) 462 writeString(h, reply.HostKey) 463 writeString(h, kp.pub[:]) 464 writeString(h, reply.EphemeralPubKey) 465 466 kInt := new(big.Int).SetBytes(secret[:]) 467 K := make([]byte, intLength(kInt)) 468 marshalInt(K, kInt) 469 h.Write(K) 470 471 return &kexResult{ 472 H: h.Sum(nil), 473 K: K, 474 HostKey: reply.HostKey, 475 Signature: reply.Signature, 476 Hash: crypto.SHA256, 477 }, nil 478 } 479 480 func (kex *curve25519sha256) Server(ctx context.Context, c packetConn, rand io.Reader, magics *handshakeMagics, priv Signer) (result *kexResult, err error) { 481 packet, err := c.readPacket(ctx) 482 if err != nil { 483 return 484 } 485 var kexInit kexECDHInitMsg 486 if err = Unmarshal(packet, &kexInit); err != nil { 487 return 488 } 489 490 if len(kexInit.ClientPubKey) != 32 { 491 return nil, errors.New("ssh: peer's curve25519 public value has wrong length") 492 } 493 494 var kp curve25519KeyPair 495 if err := kp.generate(rand); err != nil { 496 return nil, err 497 } 498 499 var clientPub, secret [32]byte 500 copy(clientPub[:], kexInit.ClientPubKey) 501 curve25519.ScalarMult(&secret, &kp.priv, &clientPub) 502 if subtle.ConstantTimeCompare(secret[:], curve25519Zeros[:]) == 1 { 503 return nil, errors.New("ssh: peer's curve25519 public value has wrong order") 504 } 505 506 hostKeyBytes := priv.PublicKey().Marshal() 507 508 h := crypto.SHA256.New() 509 magics.write(h) 510 writeString(h, hostKeyBytes) 511 writeString(h, kexInit.ClientPubKey) 512 writeString(h, kp.pub[:]) 513 514 kInt := new(big.Int).SetBytes(secret[:]) 515 K := make([]byte, intLength(kInt)) 516 marshalInt(K, kInt) 517 h.Write(K) 518 519 H := h.Sum(nil) 520 521 sig, err := signAndMarshal(priv, rand, H) 522 if err != nil { 523 return nil, err 524 } 525 526 reply := kexECDHReplyMsg{ 527 EphemeralPubKey: kp.pub[:], 528 HostKey: hostKeyBytes, 529 Signature: sig, 530 } 531 if err := c.writePacket(Marshal(&reply)); err != nil { 532 return nil, err 533 } 534 return &kexResult{ 535 H: H, 536 K: K, 537 HostKey: hostKeyBytes, 538 Signature: sig, 539 Hash: crypto.SHA256, 540 }, nil 541 }