// Copyright 2013 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package ssh import ( "crypto" "crypto/ecdsa" "crypto/elliptic" "crypto/rand" "crypto/subtle" "errors" "io" "math/big" "github.com/ScriptRock/crypto/curve25519" ) const ( kexAlgoDH1SHA1 = "diffie-hellman-group1-sha1" kexAlgoDH14SHA1 = "diffie-hellman-group14-sha1" kexAlgoDHGexSHA1 = "diffie-hellman-group-exchange-sha1" kexAlgoDHGexSHA256 = "diffie-hellman-group-exchange-sha256" kexAlgoECDH256 = "ecdh-sha2-nistp256" kexAlgoECDH384 = "ecdh-sha2-nistp384" kexAlgoECDH521 = "ecdh-sha2-nistp521" kexAlgoCurve25519SHA256 = "curve25519-sha256@libssh.org" ) // kexResult captures the outcome of a key exchange. type kexResult struct { // Session hash. See also RFC 4253, section 8. H []byte // Shared secret. See also RFC 4253, section 8. K []byte // Host key as hashed into H. HostKey []byte // Signature of H. Signature []byte // A cryptographic hash function that matches the security // level of the key exchange algorithm. It is used for // calculating H, and for deriving keys from H and K. Hash crypto.Hash // The session ID, which is the first H computed. This is used // to derive key material inside the transport. SessionID []byte } // handshakeMagics contains data that is always included in the // session hash. type handshakeMagics struct { clientVersion, serverVersion []byte clientKexInit, serverKexInit []byte } func (m *handshakeMagics) write(w io.Writer) { writeString(w, m.clientVersion) writeString(w, m.serverVersion) writeString(w, m.clientKexInit) writeString(w, m.serverKexInit) } // kexAlgorithm abstracts different key exchange algorithms. type kexAlgorithm interface { // Server runs server-side key agreement, signing the result // with a hostkey. Server(p packetConn, rand io.Reader, magics *handshakeMagics, s Signer) (*kexResult, error) // Client runs the client-side key agreement. Caller is // responsible for verifying the host key signature. Client(p packetConn, rand io.Reader, magics *handshakeMagics) (*kexResult, error) } // dhGroup is a multiplicative group suitable for implementing Diffie-Hellman key agreement. type dhGroup struct { g, p, pMinus1 *big.Int } func (group *dhGroup) bits() int { return group.p.BitLen() } func (group *dhGroup) keyPair(randSource io.Reader) (public, private *big.Int, err error) { var x *big.Int for { var err error if x, err = rand.Int(randSource, group.pMinus1); err != nil { return nil, nil, err } if x.Sign() > 0 { break } } if err != nil { return nil, nil, err } X := new(big.Int).Exp(group.g, x, group.p) return X, x, nil } func (group *dhGroup) diffieHellman(theirPublic, myPrivate *big.Int) (*big.Int, error) { if theirPublic.Cmp(bigOne) <= 0 || theirPublic.Cmp(group.pMinus1) >= 0 { return nil, errors.New("ssh: DH parameter out of bounds") } return new(big.Int).Exp(theirPublic, myPrivate, group.p), nil } func (group *dhGroup) Client(c packetConn, randSource io.Reader, magics *handshakeMagics) (*kexResult, error) { hashFunc := crypto.SHA1 var x *big.Int for { var err error if x, err = rand.Int(randSource, group.pMinus1); err != nil { return nil, err } if x.Sign() > 0 { break } } X := new(big.Int).Exp(group.g, x, group.p) kexDHInit := kexDHInitMsg{ X: X, } if err := c.writePacket(Marshal(&kexDHInit)); err != nil { return nil, err } packet, err := c.readPacket() if err != nil { return nil, err } var kexDHReply kexDHReplyMsg if err = Unmarshal(packet, &kexDHReply); err != nil { return nil, err } ki, err := group.diffieHellman(kexDHReply.Y, x) if err != nil { return nil, err } h := hashFunc.New() magics.write(h) writeString(h, kexDHReply.HostKey) writeInt(h, X) writeInt(h, kexDHReply.Y) K := make([]byte, intLength(ki)) marshalInt(K, ki) h.Write(K) return &kexResult{ H: h.Sum(nil), K: K, HostKey: kexDHReply.HostKey, Signature: kexDHReply.Signature, Hash: crypto.SHA1, }, nil } func (group *dhGroup) Server(c packetConn, randSource io.Reader, magics *handshakeMagics, priv Signer) (result *kexResult, err error) { hashFunc := crypto.SHA1 packet, err := c.readPacket() if err != nil { return } var kexDHInit kexDHInitMsg if err = Unmarshal(packet, &kexDHInit); err != nil { return } var y *big.Int for { if y, err = rand.Int(randSource, group.pMinus1); err != nil { return } if y.Sign() > 0 { break } } Y := new(big.Int).Exp(group.g, y, group.p) ki, err := group.diffieHellman(kexDHInit.X, y) if err != nil { return nil, err } hostKeyBytes := priv.PublicKey().Marshal() h := hashFunc.New() magics.write(h) writeString(h, hostKeyBytes) writeInt(h, kexDHInit.X) writeInt(h, Y) K := make([]byte, intLength(ki)) marshalInt(K, ki) h.Write(K) H := h.Sum(nil) // H is already a hash, but the hostkey signing will apply its // own key-specific hash algorithm. sig, err := signAndMarshal(priv, randSource, H) if err != nil { return nil, err } kexDHReply := kexDHReplyMsg{ HostKey: hostKeyBytes, Y: Y, Signature: sig, } packet = Marshal(&kexDHReply) err = c.writePacket(packet) return &kexResult{ H: H, K: K, HostKey: hostKeyBytes, Signature: sig, Hash: crypto.SHA1, }, nil } // ecdh performs Elliptic Curve Diffie-Hellman key exchange as // described in RFC 5656, section 4. type ecdh struct { curve elliptic.Curve } func (kex *ecdh) Client(c packetConn, rand io.Reader, magics *handshakeMagics) (*kexResult, error) { ephKey, err := ecdsa.GenerateKey(kex.curve, rand) if err != nil { return nil, err } kexInit := kexECDHInitMsg{ ClientPubKey: elliptic.Marshal(kex.curve, ephKey.PublicKey.X, ephKey.PublicKey.Y), } serialized := Marshal(&kexInit) if err := c.writePacket(serialized); err != nil { return nil, err } packet, err := c.readPacket() if err != nil { return nil, err } var reply kexECDHReplyMsg if err = Unmarshal(packet, &reply); err != nil { return nil, err } x, y, err := unmarshalECKey(kex.curve, reply.EphemeralPubKey) if err != nil { return nil, err } // generate shared secret secret, _ := kex.curve.ScalarMult(x, y, ephKey.D.Bytes()) h := ecHash(kex.curve).New() magics.write(h) writeString(h, reply.HostKey) writeString(h, kexInit.ClientPubKey) writeString(h, reply.EphemeralPubKey) K := make([]byte, intLength(secret)) marshalInt(K, secret) h.Write(K) return &kexResult{ H: h.Sum(nil), K: K, HostKey: reply.HostKey, Signature: reply.Signature, Hash: ecHash(kex.curve), }, nil } type dhGroupExchangeKexAlgo struct { hash crypto.Hash } const ( dhGroupMinimumBits = 2048 dhGroupPreferredBits = 2048 dhGroupMaximumBits = 8192 ) func (algo *dhGroupExchangeKexAlgo) Client(c packetConn, randSource io.Reader, magics *handshakeMagics) (*kexResult, error) { request := kexDHGexRequestMsg{ Min: dhGroupMinimumBits, N: dhGroupPreferredBits, Max: dhGroupMaximumBits, } serialized := Marshal(&request) if err := c.writePacket(serialized); err != nil { return nil, err } packet, err := c.readPacket() if err != nil { return nil, err } var groupMsg kexDHGexGroupMsg if err = Unmarshal(packet, &groupMsg); err != nil { return nil, err } group := &dhGroup{ g: groupMsg.G, p: groupMsg.P, pMinus1: new(big.Int).Sub(groupMsg.P, bigOne), } bits := uint32(group.bits()) if bits < request.Min || bits > request.Max { return nil, errors.New("ssh: DH group out of range") } X, x, err := group.keyPair(randSource) if err != nil { return nil, err } init := kexDHGexInitMsg{ X: X, } if err := c.writePacket(Marshal(&init)); err != nil { return nil, err } packet, err = c.readPacket() if err != nil { return nil, err } var reply kexDHGexReplyMsg if err = Unmarshal(packet, &reply); err != nil { return nil, err } kInt, err := group.diffieHellman(reply.Y, x) if err != nil { return nil, err } h := algo.hash.New() magics.write(h) writeString(h, reply.HostKey) var int32Buf = make([]byte, 4) marshalUint32(int32Buf, request.Min) h.Write(int32Buf) marshalUint32(int32Buf, request.N) h.Write(int32Buf) marshalUint32(int32Buf, request.Max) h.Write(int32Buf) writeInt(h, group.p) writeInt(h, group.g) writeInt(h, X) writeInt(h, reply.Y) K := make([]byte, intLength(kInt)) marshalInt(K, kInt) h.Write(K) return &kexResult{ H: h.Sum(nil), K: K, HostKey: reply.HostKey, Signature: reply.Signature, Hash: algo.hash, }, nil } func chooseDHGroup(min, n, max uint32) (*dhGroup, error) { if n < dhGroupMinimumBits { n = dhGroupMinimumBits } else if n > dhGroupMaximumBits { n = dhGroupMaximumBits } if n < min || n > max { return nil, errors.New("ssh: DH group out of range") } // For now we only use the constant groups from the RFCs // TODO: Use the moduli file /etc/ssh/moduli if it exists. for _, group := range []*dhGroup{&dhGroup14, &dhGroup16, &dhGroup18} { if n <= uint32(group.bits()) { return group, nil } } // We should never get here as long as dhGroupMaximumBits <= dhGroup18.bits(), which it is. panic("NOTREACHED") } func (algo *dhGroupExchangeKexAlgo) Server(c packetConn, randSource io.Reader, magics *handshakeMagics, priv Signer) (result *kexResult, err error) { packet, err := c.readPacket() if err != nil { return nil, err } var request kexDHGexRequestMsg if err = Unmarshal(packet, &request); err != nil { return nil, err } group, err := chooseDHGroup(request.Min, request.N, request.Max) if err != nil { return nil, err } groupMsg := kexDHGexGroupMsg{ P: group.p, G: group.g, } serialized := Marshal(&groupMsg) if err := c.writePacket(serialized); err != nil { return nil, err } packet, err = c.readPacket() if err != nil { return } var init kexDHGexInitMsg if err = Unmarshal(packet, &init); err != nil { return } Y, y, err := group.keyPair(randSource) if err != nil { return nil, err } kInt, err := group.diffieHellman(init.X, y) if err != nil { return nil, err } hostKeyBytes := priv.PublicKey().Marshal() h := algo.hash.New() magics.write(h) writeString(h, hostKeyBytes) var int32Buf = make([]byte, 4) marshalUint32(int32Buf, request.Min) h.Write(int32Buf) marshalUint32(int32Buf, request.N) h.Write(int32Buf) marshalUint32(int32Buf, request.Max) h.Write(int32Buf) writeInt(h, group.p) writeInt(h, group.g) writeInt(h, init.X) writeInt(h, Y) K := make([]byte, intLength(kInt)) marshalInt(K, kInt) h.Write(K) H := h.Sum(nil) // H is already a hash, but the hostkey signing will apply its // own key-specific hash algorithm. sig, err := signAndMarshal(priv, randSource, H) if err != nil { return nil, err } reply := kexDHGexReplyMsg{ HostKey: hostKeyBytes, Y: Y, Signature: sig, } packet = Marshal(&reply) err = c.writePacket(packet) if err != nil { return nil, err } return &kexResult{ H: H, K: K, HostKey: hostKeyBytes, Signature: sig, Hash: algo.hash, }, nil } // unmarshalECKey parses and checks an EC key. func unmarshalECKey(curve elliptic.Curve, pubkey []byte) (x, y *big.Int, err error) { x, y = elliptic.Unmarshal(curve, pubkey) if x == nil { return nil, nil, errors.New("ssh: elliptic.Unmarshal failure") } if !validateECPublicKey(curve, x, y) { return nil, nil, errors.New("ssh: public key not on curve") } return x, y, nil } // validateECPublicKey checks that the point is a valid public key for // the given curve. See [SEC1], 3.2.2 func validateECPublicKey(curve elliptic.Curve, x, y *big.Int) bool { if x.Sign() == 0 && y.Sign() == 0 { return false } if x.Cmp(curve.Params().P) >= 0 { return false } if y.Cmp(curve.Params().P) >= 0 { return false } if !curve.IsOnCurve(x, y) { return false } // We don't check if N * PubKey == 0, since // // - the NIST curves have cofactor = 1, so this is implicit. // (We don't foresee an implementation that supports non NIST // curves) // // - for ephemeral keys, we don't need to worry about small // subgroup attacks. return true } func (kex *ecdh) Server(c packetConn, rand io.Reader, magics *handshakeMagics, priv Signer) (result *kexResult, err error) { packet, err := c.readPacket() if err != nil { return nil, err } var kexECDHInit kexECDHInitMsg if err = Unmarshal(packet, &kexECDHInit); err != nil { return nil, err } clientX, clientY, err := unmarshalECKey(kex.curve, kexECDHInit.ClientPubKey) if err != nil { return nil, err } // We could cache this key across multiple users/multiple // connection attempts, but the benefit is small. OpenSSH // generates a new key for each incoming connection. ephKey, err := ecdsa.GenerateKey(kex.curve, rand) if err != nil { return nil, err } hostKeyBytes := priv.PublicKey().Marshal() serializedEphKey := elliptic.Marshal(kex.curve, ephKey.PublicKey.X, ephKey.PublicKey.Y) // generate shared secret secret, _ := kex.curve.ScalarMult(clientX, clientY, ephKey.D.Bytes()) h := ecHash(kex.curve).New() magics.write(h) writeString(h, hostKeyBytes) writeString(h, kexECDHInit.ClientPubKey) writeString(h, serializedEphKey) K := make([]byte, intLength(secret)) marshalInt(K, secret) h.Write(K) H := h.Sum(nil) // H is already a hash, but the hostkey signing will apply its // own key-specific hash algorithm. sig, err := signAndMarshal(priv, rand, H) if err != nil { return nil, err } reply := kexECDHReplyMsg{ EphemeralPubKey: serializedEphKey, HostKey: hostKeyBytes, Signature: sig, } serialized := Marshal(&reply) if err := c.writePacket(serialized); err != nil { return nil, err } return &kexResult{ H: H, K: K, HostKey: reply.HostKey, Signature: sig, Hash: ecHash(kex.curve), }, nil } var kexAlgoMap = map[string]kexAlgorithm{} var ( dhGroup1 = dhGroup{} dhGroup14 = dhGroup{} dhGroup16 = dhGroup{} dhGroup18 = dhGroup{} ) func init() { // This is the group called diffie-hellman-group1-sha1 in RFC // 4253 and Oakley Group 2 in RFC 2409. p, _ := new(big.Int).SetString("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF", 16) dhGroup1 = dhGroup{ g: new(big.Int).SetInt64(2), p: p, pMinus1: new(big.Int).Sub(p, bigOne), } // This is the group called diffie-hellman-group14-sha1 in RFC // 4253 and Oakley Group 14 in RFC 3526. p, _ = new(big.Int).SetString("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF", 16) dhGroup14 = dhGroup{ g: new(big.Int).SetInt64(2), p: p, pMinus1: new(big.Int).Sub(p, bigOne), } // 4096-bit MODP group - Group 16 in RFC 3526. p, _ = new(big.Int).SetString("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934063199FFFFFFFFFFFFFFFF", 16) dhGroup16 = dhGroup{ g: new(big.Int).SetInt64(2), p: p, pMinus1: new(big.Int).Sub(p, bigOne), } // 8192-bit MODP group - Group 18 in RFC 3526. p, _ = new(big.Int).SetString("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C93402849236C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BDF8FF9406AD9E530EE5DB382F413001AEB06A53ED9027D831179727B0865A8918DA3EDBEBCF9B14ED44CE6CBACED4BB1BDB7F1447E6CC254B332051512BD7AF426FB8F401378CD2BF5983CA01C64B92ECF032EA15D1721D03F482D7CE6E74FEF6D55E702F46980C82B5A84031900B1C9E59E7C97FBEC7E8F323A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AACC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE32806A1D58BB7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55CDA56C9EC2EF29632387FE8D76E3C0468043E8F663F4860EE12BF2D5B0B7474D6E694F91E6DBE115974A3926F12FEE5E438777CB6A932DF8CD8BEC4D073B931BA3BC832B68D9DD300741FA7BF8AFC47ED2576F6936BA424663AAB639C5AE4F5683423B4742BF1C978238F16CBE39D652DE3FDB8BEFC848AD922222E04A4037C0713EB57A81A23F0C73473FC646CEA306B4BCBC8862F8385DDFA9D4B7FA2C087E879683303ED5BDD3A062B3CF5B3A278A66D2A13F83F44F82DDF310EE074AB6A364597E899A0255DC164F31CC50846851DF9AB48195DED7EA1B1D510BD7EE74D73FAF36BC31ECFA268359046F4EB879F924009438B481C6CD7889A002ED5EE382BC9190DA6FC026E479558E4475677E9AA9E3050E2765694DFC81F56E880B96E7160C980DD98EDD3DFFFFFFFFFFFFFFFFF", 16) dhGroup18 = dhGroup{ g: new(big.Int).SetInt64(2), p: p, pMinus1: new(big.Int).Sub(p, bigOne), } kexAlgoMap[kexAlgoDH1SHA1] = &dhGroup1 kexAlgoMap[kexAlgoDH14SHA1] = &dhGroup14 kexAlgoMap[kexAlgoDHGexSHA1] = &dhGroupExchangeKexAlgo{crypto.SHA1} kexAlgoMap[kexAlgoDHGexSHA256] = &dhGroupExchangeKexAlgo{crypto.SHA256} kexAlgoMap[kexAlgoECDH521] = &ecdh{elliptic.P521()} kexAlgoMap[kexAlgoECDH384] = &ecdh{elliptic.P384()} kexAlgoMap[kexAlgoECDH256] = &ecdh{elliptic.P256()} kexAlgoMap[kexAlgoCurve25519SHA256] = &curve25519sha256{} } // curve25519sha256 implements the curve25519-sha256@libssh.org key // agreement protocol, as described in // https://git.libssh.org/projects/libssh.git/tree/doc/curve25519-sha256@libssh.org.txt type curve25519sha256 struct{} type curve25519KeyPair struct { priv [32]byte pub [32]byte } func (kp *curve25519KeyPair) generate(rand io.Reader) error { if _, err := io.ReadFull(rand, kp.priv[:]); err != nil { return err } curve25519.ScalarBaseMult(&kp.pub, &kp.priv) return nil } // curve25519Zeros is just an array of 32 zero bytes so that we have something // convenient to compare against in order to reject curve25519 points with the // wrong order. var curve25519Zeros [32]byte func (kex *curve25519sha256) Client(c packetConn, rand io.Reader, magics *handshakeMagics) (*kexResult, error) { var kp curve25519KeyPair if err := kp.generate(rand); err != nil { return nil, err } if err := c.writePacket(Marshal(&kexECDHInitMsg{kp.pub[:]})); err != nil { return nil, err } packet, err := c.readPacket() if err != nil { return nil, err } var reply kexECDHReplyMsg if err = Unmarshal(packet, &reply); err != nil { return nil, err } if len(reply.EphemeralPubKey) != 32 { return nil, errors.New("ssh: peer's curve25519 public value has wrong length") } var servPub, secret [32]byte copy(servPub[:], reply.EphemeralPubKey) curve25519.ScalarMult(&secret, &kp.priv, &servPub) if subtle.ConstantTimeCompare(secret[:], curve25519Zeros[:]) == 1 { return nil, errors.New("ssh: peer's curve25519 public value has wrong order") } h := crypto.SHA256.New() magics.write(h) writeString(h, reply.HostKey) writeString(h, kp.pub[:]) writeString(h, reply.EphemeralPubKey) ki := new(big.Int).SetBytes(secret[:]) K := make([]byte, intLength(ki)) marshalInt(K, ki) h.Write(K) return &kexResult{ H: h.Sum(nil), K: K, HostKey: reply.HostKey, Signature: reply.Signature, Hash: crypto.SHA256, }, nil } func (kex *curve25519sha256) Server(c packetConn, rand io.Reader, magics *handshakeMagics, priv Signer) (result *kexResult, err error) { packet, err := c.readPacket() if err != nil { return } var kexInit kexECDHInitMsg if err = Unmarshal(packet, &kexInit); err != nil { return } if len(kexInit.ClientPubKey) != 32 { return nil, errors.New("ssh: peer's curve25519 public value has wrong length") } var kp curve25519KeyPair if err := kp.generate(rand); err != nil { return nil, err } var clientPub, secret [32]byte copy(clientPub[:], kexInit.ClientPubKey) curve25519.ScalarMult(&secret, &kp.priv, &clientPub) if subtle.ConstantTimeCompare(secret[:], curve25519Zeros[:]) == 1 { return nil, errors.New("ssh: peer's curve25519 public value has wrong order") } hostKeyBytes := priv.PublicKey().Marshal() h := crypto.SHA256.New() magics.write(h) writeString(h, hostKeyBytes) writeString(h, kexInit.ClientPubKey) writeString(h, kp.pub[:]) ki := new(big.Int).SetBytes(secret[:]) K := make([]byte, intLength(ki)) marshalInt(K, ki) h.Write(K) H := h.Sum(nil) sig, err := signAndMarshal(priv, rand, H) if err != nil { return nil, err } reply := kexECDHReplyMsg{ EphemeralPubKey: kp.pub[:], HostKey: hostKeyBytes, Signature: sig, } if err := c.writePacket(Marshal(&reply)); err != nil { return nil, err } return &kexResult{ H: H, K: K, HostKey: hostKeyBytes, Signature: sig, Hash: crypto.SHA256, }, nil }