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