+ ssh_pkt_getstring(pktin, &s->sigdata, &s->siglen);
+ if (!s->sigdata) {
+ bombout(("unable to parse key exchange reply packet"));
+ crStopV;
+ }
+
+ ssh_ecdhkex_freekey(s->eckey);
+ } else {
+ logeventf(ssh, "Doing RSA key exchange with hash %s",
+ ssh->kex->hash->text_name);
+ ssh->pkt_kctx = SSH2_PKTCTX_RSAKEX;
+ /*
+ * RSA key exchange. First expect a KEXRSA_PUBKEY packet
+ * from the server.
+ */
+ crWaitUntilV(pktin);
+ if (pktin->type != SSH2_MSG_KEXRSA_PUBKEY) {
+ bombout(("expected RSA public key packet from server"));
+ crStopV;
+ }
+
+ ssh_pkt_getstring(pktin, &s->hostkeydata, &s->hostkeylen);
+ if (!s->hostkeydata) {
+ bombout(("unable to parse RSA public key packet"));
+ crStopV;
+ }
+ hash_string(ssh->kex->hash, ssh->exhash,
+ s->hostkeydata, s->hostkeylen);
+ s->hkey = ssh->hostkey->newkey(ssh->hostkey,
+ s->hostkeydata, s->hostkeylen);
+
+ {
+ char *keydata;
+ ssh_pkt_getstring(pktin, &keydata, &s->rsakeylen);
+ if (!keydata) {
+ bombout(("unable to parse RSA public key packet"));
+ crStopV;
+ }
+ s->rsakeydata = snewn(s->rsakeylen, char);
+ memcpy(s->rsakeydata, keydata, s->rsakeylen);
+ }
+
+ s->rsakey = ssh_rsakex_newkey(s->rsakeydata, s->rsakeylen);
+ if (!s->rsakey) {
+ sfree(s->rsakeydata);
+ bombout(("unable to parse RSA public key from server"));
+ crStopV;
+ }
+
+ hash_string(ssh->kex->hash, ssh->exhash, s->rsakeydata, s->rsakeylen);
+
+ /*
+ * Next, set up a shared secret K, of precisely KLEN -
+ * 2*HLEN - 49 bits, where KLEN is the bit length of the
+ * RSA key modulus and HLEN is the bit length of the hash
+ * we're using.
+ */
+ {
+ int klen = ssh_rsakex_klen(s->rsakey);
+ int nbits = klen - (2*ssh->kex->hash->hlen*8 + 49);
+ int i, byte = 0;
+ unsigned char *kstr1, *kstr2, *outstr;
+ int kstr1len, kstr2len, outstrlen;
+
+ s->K = bn_power_2(nbits - 1);
+
+ for (i = 0; i < nbits; i++) {
+ if ((i & 7) == 0) {
+ byte = random_byte();
+ }
+ bignum_set_bit(s->K, i, (byte >> (i & 7)) & 1);
+ }
+
+ /*
+ * Encode this as an mpint.
+ */
+ kstr1 = ssh2_mpint_fmt(s->K, &kstr1len);
+ kstr2 = snewn(kstr2len = 4 + kstr1len, unsigned char);
+ PUT_32BIT(kstr2, kstr1len);
+ memcpy(kstr2 + 4, kstr1, kstr1len);
+
+ /*
+ * Encrypt it with the given RSA key.
+ */
+ outstrlen = (klen + 7) / 8;
+ outstr = snewn(outstrlen, unsigned char);
+ ssh_rsakex_encrypt(ssh->kex->hash, kstr2, kstr2len,
+ outstr, outstrlen, s->rsakey);
+
+ /*
+ * And send it off in a return packet.
+ */
+ s->pktout = ssh2_pkt_init(SSH2_MSG_KEXRSA_SECRET);
+ ssh2_pkt_addstring_start(s->pktout);
+ ssh2_pkt_addstring_data(s->pktout, (char *)outstr, outstrlen);
+ ssh2_pkt_send_noqueue(ssh, s->pktout);
+
+ hash_string(ssh->kex->hash, ssh->exhash, outstr, outstrlen);
+
+ sfree(kstr2);
+ sfree(kstr1);
+ sfree(outstr);
+ }
+
+ ssh_rsakex_freekey(s->rsakey);
+
+ crWaitUntilV(pktin);
+ if (pktin->type != SSH2_MSG_KEXRSA_DONE) {
+ sfree(s->rsakeydata);
+ bombout(("expected signature packet from server"));
+ crStopV;
+ }
+
+ ssh_pkt_getstring(pktin, &s->sigdata, &s->siglen);
+ if (!s->sigdata) {
+ bombout(("unable to parse signature packet"));
+ crStopV;
+ }
+
+ sfree(s->rsakeydata);
+ }
+
+ hash_mpint(ssh->kex->hash, ssh->exhash, s->K);
+ assert(ssh->kex->hash->hlen <= sizeof(s->exchange_hash));
+ ssh->kex->hash->final(ssh->exhash, s->exchange_hash);
+
+ ssh->kex_ctx = NULL;
+
+#if 0
+ debug(("Exchange hash is:\n"));
+ dmemdump(s->exchange_hash, ssh->kex->hash->hlen);
+#endif
+
+ if (!s->hkey) {
+ bombout(("Server's host key is invalid"));
+ crStopV;
+ }
+
+ if (!ssh->hostkey->verifysig(s->hkey, s->sigdata, s->siglen,
+ (char *)s->exchange_hash,
+ ssh->kex->hash->hlen)) {
+#ifndef FUZZING
+ bombout(("Server's host key did not match the signature supplied"));
+ crStopV;
+#endif
+ }
+
+ s->keystr = ssh->hostkey->fmtkey(s->hkey);
+ if (!s->got_session_id) {
+ /*
+ * Make a note of any host key format we'd have preferred to use,
+ * had we already known the corresponding keys.
+ */
+ {
+ int i, j = 0;
+ char *list = NULL;
+ for (i = 0; i < lenof(hostkey_algs); i++) {
+ if (hostkey_algs[i].alg == ssh->hostkey)
+ /* Not worth mentioning key types we wouldn't use */
+ break;
+ else if (ssh->uncert_hostkeys[j] == i) {
+ char *newlist;
+ if (list)
+ newlist = dupprintf("%s/%s", list,
+ hostkey_algs[i].alg->name);
+ else
+ newlist = dupprintf("%s", hostkey_algs[i].alg->name);
+ sfree(list);
+ list = newlist;
+ j++;
+ /* Assumes that hostkey_algs and uncert_hostkeys are
+ * sorted in the same order */
+ if (j == ssh->n_uncert_hostkeys)
+ break;
+ else
+ assert(ssh->uncert_hostkeys[j] >
+ ssh->uncert_hostkeys[j-1]);
+ }
+ }
+ if (list) {
+ logeventf(ssh,
+ "Server has %s host key%s, but we don't know %s; "
+ "using %s instead",
+ list, j ? "s" : "", j ? "any of them" : "it",
+ ssh->hostkey->name);
+ sfree(list);
+ }
+ }
+
+ /*
+ * Authenticate remote host: verify host key. (We've already
+ * checked the signature of the exchange hash.)
+ */
+ s->fingerprint = ssh2_fingerprint(ssh->hostkey, s->hkey);
+ logevent("Host key fingerprint is:");
+ logevent(s->fingerprint);
+ /* First check against manually configured host keys. */
+ s->dlgret = verify_ssh_manual_host_key(ssh, s->fingerprint,
+ ssh->hostkey, s->hkey);
+ if (s->dlgret == 0) { /* did not match */
+ bombout(("Host key did not appear in manually configured list"));
+ crStopV;
+ } else if (s->dlgret < 0) { /* none configured; use standard handling */
+ ssh_set_frozen(ssh, 1);
+ s->dlgret = verify_ssh_host_key(ssh->frontend,
+ ssh->savedhost, ssh->savedport,
+ ssh->hostkey->keytype, s->keystr,
+ s->fingerprint,
+ ssh_dialog_callback, ssh);
+#ifdef FUZZING
+ s->dlgret = 1;
+#endif
+ if (s->dlgret < 0) {
+ do {
+ crReturnV;
+ if (pktin) {
+ bombout(("Unexpected data from server while waiting"
+ " for user host key response"));
+ crStopV;
+ }
+ } while (pktin || inlen > 0);
+ s->dlgret = ssh->user_response;
+ }
+ ssh_set_frozen(ssh, 0);
+ if (s->dlgret == 0) {
+ ssh_disconnect(ssh, "Aborted at host key verification", NULL,
+ 0, TRUE);
+ crStopV;
+ }
+ }
+ sfree(s->fingerprint);
+ /*
+ * Save this host key, to check against the one presented in
+ * subsequent rekeys.
+ */
+ ssh->hostkey_str = s->keystr;
+ } else if (ssh->cross_certifying) {
+ s->fingerprint = ssh2_fingerprint(ssh->hostkey, s->hkey);
+ logevent("Storing additional host key for this host:");
+ logevent(s->fingerprint);
+ store_host_key(ssh->savedhost, ssh->savedport,
+ ssh->hostkey->keytype, s->keystr);
+ ssh->cross_certifying = FALSE;
+ /*
+ * Don't forget to store the new key as the one we'll be
+ * re-checking in future normal rekeys.
+ */
+ ssh->hostkey_str = s->keystr;
+ } else {
+ /*
+ * In a rekey, we never present an interactive host key
+ * verification request to the user. Instead, we simply
+ * enforce that the key we're seeing this time is identical to
+ * the one we saw before.
+ */
+ if (strcmp(ssh->hostkey_str, s->keystr)) {
+#ifndef FUZZING
+ bombout(("Host key was different in repeat key exchange"));
+ crStopV;
+#endif
+ }
+ sfree(s->keystr);
+ }
+ ssh->hostkey->freekey(s->hkey);
+
+ /*
+ * The exchange hash from the very first key exchange is also
+ * the session id, used in session key construction and
+ * authentication.