Index: config.c
===================================================================
--- config.c	(revision 6118)
+++ config.c	(working copy)
@@ -163,6 +163,7 @@
 	    { "Diffie-Hellman group 1",		KEX_DHGROUP1 },
 	    { "Diffie-Hellman group 14",	KEX_DHGROUP14 },
 	    { "Diffie-Hellman group exchange",	KEX_DHGEX },
+	    { "RSA-based key exchange", 	KEX_RSA },
 	    { "-- warn below here --",		KEX_WARN }
 	};
 
Index: sshdh.c
===================================================================
--- sshdh.c	(revision 6118)
+++ sshdh.c	(working copy)
@@ -48,17 +48,17 @@
 
 const struct ssh_kex ssh_diffiehellman_group1 = {
     "diffie-hellman-group1-sha1", "group1",
-    P1, G, lenof(P1), lenof(G)
+    KEXTYPE_DH, P1, G, lenof(P1), lenof(G)
 };
 
 const struct ssh_kex ssh_diffiehellman_group14 = {
     "diffie-hellman-group14-sha1", "group14",
-    P14, G, lenof(P14), lenof(G)
+    KEXTYPE_DH, P14, G, lenof(P14), lenof(G)
 };
 
 const struct ssh_kex ssh_diffiehellman_gex = {
     "diffie-hellman-group-exchange-sha1", NULL,
-    NULL, NULL, 0, 0
+    KEXTYPE_DH, NULL, NULL, 0, 0
 };
 
 /*
Index: settings.c
===================================================================
--- settings.c	(revision 6118)
+++ settings.c	(working copy)
@@ -26,6 +26,7 @@
     { "dh-gex-sha1",	    KEX_DHGEX },
     { "dh-group14-sha1",    KEX_DHGROUP14 },
     { "dh-group1-sha1",	    KEX_DHGROUP1 },
+    { "rsa-sha1",	    KEX_RSA },
     { "WARN",		    KEX_WARN }
 };
 
@@ -562,9 +563,9 @@
 	char *default_kexes;
 	gppi(sesskey, "BugDHGEx2", 0, &i); i = 2-i;
 	if (i == FORCE_ON)
-	    default_kexes = "dh-group14-sha1,dh-group1-sha1,WARN,dh-gex-sha1";
+	    default_kexes = "dh-group14-sha1,dh-group1-sha1,rsa-sha1,WARN,dh-gex-sha1";
 	else
-	    default_kexes = "dh-gex-sha1,dh-group14-sha1,dh-group1-sha1,WARN";
+	    default_kexes = "dh-gex-sha1,dh-group14-sha1,dh-group1-sha1,rsa-sha1,WARN";
 	gprefs(sesskey, "KEX", default_kexes,
 	       kexnames, KEX_MAX, cfg->ssh_kexlist);
     }
Index: sshrsa.c
===================================================================
--- sshrsa.c	(revision 6118)
+++ sshrsa.c	(working copy)
@@ -836,3 +836,137 @@
     "ssh-rsa",
     "rsa2"
 };
+
+void *ssh_rsakex_newkey(char *data, int len)
+{
+    return rsa2_newkey(data, len);
+}
+
+void ssh_rsakex_freekey(void *key)
+{
+    rsa2_freekey(key);
+}
+
+int ssh_rsakex_klen(void *key)
+{
+    struct RSAKey *rsa = (struct RSAKey *) key;
+
+    return bignum_bitcount(rsa->modulus);
+}
+
+static void oaep_mask(void *seed, int seedlen, void *vdata, int datalen)
+{
+    unsigned char *data = (unsigned char *)vdata;
+    unsigned count = 0;
+
+    while (datalen > 0) {
+        int i, max = (datalen > 20 ? 20 : datalen);
+        SHA_State s;
+        unsigned char counter[4], hash[20];
+
+        PUT_32BIT(counter, count);
+        SHA_Init(&s);
+        SHA_Bytes(&s, seed, seedlen);
+        SHA_Bytes(&s, counter, 4);
+        SHA_Final(&s, hash);
+        count++;
+
+        for (i = 0; i < max; i++)
+            data[i] ^= hash[i];
+
+        data += max;
+        datalen -= max;
+    }
+}
+
+void ssh_rsakex_encrypt(unsigned char *in, int inlen,
+                        unsigned char *out, int outlen,
+                        void *key)
+{
+    Bignum b1, b2;
+    struct RSAKey *rsa = (struct RSAKey *) key;
+    int k, i;
+    char *p;
+    const int HLEN = 20;               /* we currently only use SHA-1 */
+
+    /*
+     * Here we encrypt using RSAES-OAEP. Essentially this means:
+     * 
+     *  - we have a SHA-based `mask generation function' which
+     *    creates a pseudo-random stream of mask data
+     *    deterministically from an input chunk of data.
+     * 
+     *  - we have a random chunk of data called a seed.
+     * 
+     *  - we use the seed to generate a mask which we XOR with our
+     *    plaintext.
+     * 
+     *  - then we use _the masked plaintext_ to generate a mask
+     *    which we XOR with the seed.
+     * 
+     *  - then we concatenate the masked seed and the masked
+     *    plaintext, and RSA-encrypt that lot.
+     * 
+     * The result is that the data input to the encryption function
+     * is random-looking and (hopefully) contains no exploitable
+     * structure such as PKCS1-v1_5 does.
+     * 
+     * For a precise specification, see RFC 3447, section 7.1.1.
+     * Some of the variable names below are derived from that, so
+     * it'd probably help to read it anyway.
+     */
+
+    /* k denotes the length in octets of the RSA modulus. */
+    k = (7 + bignum_bitcount(rsa->modulus)) / 8;
+
+    /* The length of the input data must be at most k - 2hLen - 2. */
+    assert(inlen > 0 && inlen <= k - 2*HLEN - 2);
+
+    /* The length of the output data wants to be precisely k. */
+    assert(outlen == k);
+
+    /*
+     * Now perform EME-OAEP encoding. First set up all the unmasked
+     * output data.
+     */
+    /* Leading byte zero. */
+    out[0] = 0;
+    /* At position 1, the seed: HLEN bytes of random data. */
+    for (i = 0; i < HLEN; i++)
+        out[i + 1] = random_byte();
+    /* At position 1+HLEN, the data block DB, consisting of: */
+    /* The hash of the label (we only support an empty label here) */
+    SHA_Simple("", 0, out + HLEN + 1);
+    /* A bunch of zero octets */
+    memset(out + 2*HLEN + 1, 0, outlen - (2*HLEN + 1));
+    /* A single 1 octet, followed by the input message data. */
+    out[outlen - inlen - 1] = 1;
+    memcpy(out + outlen - inlen, in, inlen);
+
+    /*
+     * Now use the seed data to mask the block DB.
+     */
+    oaep_mask(out+1, HLEN, out+HLEN+1, outlen-HLEN-1);
+
+    /*
+     * And now use the masked DB to mask the seed itself.
+     */
+    oaep_mask(out+HLEN+1, outlen-HLEN-1, out+1, HLEN);
+
+    /*
+     * Now `out' contains precisely the data we want to
+     * RSA-encrypt.
+     */
+    b1 = bignum_from_bytes(out, outlen);
+    b2 = modpow(b1, rsa->exponent, rsa->modulus);
+    p = out;
+    for (i = outlen; i--;) {
+	*p++ = bignum_byte(b2, i);
+    }
+    freebn(b1);
+    freebn(b2);
+
+    /*
+     * And we're done.
+     */
+}
Index: ssh.c
===================================================================
--- ssh.c	(revision 6118)
+++ ssh.c	(working copy)
@@ -77,6 +77,9 @@
 #define SSH2_MSG_KEX_DH_GEX_GROUP                 31	/* 0x1f */
 #define SSH2_MSG_KEX_DH_GEX_INIT                  32	/* 0x20 */
 #define SSH2_MSG_KEX_DH_GEX_REPLY                 33	/* 0x21 */
+#define SSH2_MSG_KEXRSA_PUBKEY                    30    /* 0x1e */
+#define SSH2_MSG_KEXRSA_SECRET                    31    /* 0x1f */
+#define SSH2_MSG_KEXRSA_DONE                      32    /* 0x20 */
 #define SSH2_MSG_USERAUTH_REQUEST                 50	/* 0x32 */
 #define SSH2_MSG_USERAUTH_FAILURE                 51	/* 0x33 */
 #define SSH2_MSG_USERAUTH_SUCCESS                 52	/* 0x34 */
@@ -106,6 +109,7 @@
  */
 #define SSH2_PKTCTX_DHGROUP          0x0001
 #define SSH2_PKTCTX_DHGEX            0x0002
+#define SSH2_PKTCTX_RSAKEX           0x0004
 #define SSH2_PKTCTX_KEX_MASK         0x000F
 #define SSH2_PKTCTX_PUBLICKEY        0x0010
 #define SSH2_PKTCTX_PASSWORD         0x0020
@@ -333,6 +337,9 @@
     translatec(SSH2_MSG_KEX_DH_GEX_GROUP, SSH2_PKTCTX_DHGEX);
     translatec(SSH2_MSG_KEX_DH_GEX_INIT, SSH2_PKTCTX_DHGEX);
     translatec(SSH2_MSG_KEX_DH_GEX_REPLY, SSH2_PKTCTX_DHGEX);
+    translatec(SSH2_MSG_KEXRSA_PUBKEY, SSH2_PKTCTX_RSAKEX);
+    translatec(SSH2_MSG_KEXRSA_SECRET, SSH2_PKTCTX_RSAKEX);
+    translatec(SSH2_MSG_KEXRSA_DONE, SSH2_PKTCTX_RSAKEX);
     translate(SSH2_MSG_USERAUTH_REQUEST);
     translate(SSH2_MSG_USERAUTH_FAILURE);
     translate(SSH2_MSG_USERAUTH_SUCCESS);
@@ -4815,6 +4822,11 @@
     crFinishV;
 }
 
+const struct ssh_kex ssh_rsa_kex = {
+    "rsa1024-sha1-draft-03@putty.projects.tartarus.org", NULL,
+    KEXTYPE_RSA, NULL, NULL, 0, 0
+};
+
 /*
  * Handle the top-level SSH-2 protocol.
  */
@@ -4979,9 +4991,10 @@
 	const struct ssh_mac *scmac_tobe;
 	const struct ssh_compress *cscomp_tobe;
 	const struct ssh_compress *sccomp_tobe;
-	char *hostkeydata, *sigdata, *keystr, *fingerprint;
-	int hostkeylen, siglen;
+	char *hostkeydata, *sigdata, *rsakeydata, *keystr, *fingerprint;
+	int hostkeylen, siglen, rsakeylen;
 	void *hkey;		       /* actual host key */
+        void *rsakey;                  /* for RSA kex */
 	unsigned char exchange_hash[20];
 	int n_preferred_kex;
 	const struct ssh_kex *preferred_kex[KEX_MAX];
@@ -5035,6 +5048,10 @@
 		s->preferred_kex[s->n_preferred_kex++] =
 		    &ssh_diffiehellman_group1;
 		break;
+	      case KEX_RSA:
+		s->preferred_kex[s->n_preferred_kex++] =
+		    &ssh_rsa_kex;
+		break;
 	      case CIPHER_WARN:
 		/* Flag for later. Don't bother if it's the last in
 		 * the list. */
@@ -5419,6 +5436,8 @@
 	    crWaitUntil(pktin);                /* Ignore packet */
     }
 
+    if (ssh->kex->main_type == KEXTYPE_DH) {
+	/* XXX The lines below should be reindented before this is committed.*/
     /*
      * Work out the number of bits of key we will need from the key
      * exchange. We start with the maximum key length of either
@@ -5514,10 +5533,114 @@
     }
     sha_mpint(&ssh->exhash, s->e);
     sha_mpint(&ssh->exhash, s->f);
+
+    dh_cleanup(ssh->kex_ctx);
+    freebn(s->f);
+    if (ssh->kex == &ssh_diffiehellman_gex) {
+        freebn(s->g);
+	freebn(s->p);
+    }
+        /* XXX end incorrectly-indented section */
+    } else {
+	ssh->pkt_ctx |= SSH2_PKTCTX_RSAKEX;
+        /*
+         * RSA key exchange. First expect a KEXRSA_PUBKEY packet
+         * from the server.
+         */
+        crWaitUntil(pktin);
+        if (pktin->type != SSH2_MSG_KEXRSA_PUBKEY) {
+            bombout(("expected RSA public key packet from server"));
+            crStop(0);
+        }
+
+        {
+            char *keydata;
+            ssh_pkt_getstring(pktin, &keydata, &s->rsakeylen);
+            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"));
+            crStop(0);
+        }
+
+        ssh_pkt_getstring(pktin, &s->hostkeydata, &s->hostkeylen);
+
+        sha_string(&ssh->exhash, s->hostkeydata, s->hostkeylen);
+        sha_string(&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 (currently always SHA-1).
+         */
+        {
+            int klen = ssh_rsakex_klen(s->rsakey);
+            int nbits = klen - (2*160 + 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(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, outstr, outstrlen);
+            ssh2_pkt_send_noqueue(ssh, s->pktout);
+
+	    sha_string(&ssh->exhash, outstr, outstrlen);
+
+            sfree(kstr2);
+            sfree(kstr1);
+            sfree(outstr);
+        }
+
+        ssh_rsakex_freekey(s->rsakey);
+
+        crWaitUntil(pktin);
+        if (pktin->type != SSH2_MSG_KEXRSA_DONE) {
+            sfree(s->rsakeydata);
+            bombout(("expected signature packet from server"));
+            crStop(0);
+        }
+
+        ssh_pkt_getstring(pktin, &s->sigdata, &s->siglen);
+
+        sfree(s->rsakeydata);
+    }
+
     sha_mpint(&ssh->exhash, s->K);
     SHA_Final(&ssh->exhash, s->exchange_hash);
 
-    dh_cleanup(ssh->kex_ctx);
     ssh->kex_ctx = NULL;
 
 #if 0
@@ -5687,14 +5810,9 @@
 		  ssh->sccomp->text_name);
 
     /*
-     * Free key exchange data.
+     * Free shared secret.
      */
-    freebn(s->f);
     freebn(s->K);
-    if (ssh->kex == &ssh_diffiehellman_gex) {
-	freebn(s->g);
-	freebn(s->p);
-    }
 
     /*
      * Key exchange is over. Loop straight back round if we have a
Index: ssh.h
===================================================================
--- ssh.h	(revision 6118)
+++ ssh.h	(working copy)
@@ -82,6 +82,16 @@
 int detect_attack(void *handle, unsigned char *buf, uint32 len,
 		  unsigned char *IV);
 
+/*
+ * SSH2 RSA key exchange functions
+ */
+void *ssh_rsakex_newkey(char *data, int len);
+void ssh_rsakex_freekey(void *key);
+int ssh_rsakex_klen(void *key);
+void ssh_rsakex_encrypt(unsigned char *in, int inlen,
+                        unsigned char *out, int outlen,
+                        void *key);
+
 typedef struct {
     uint32 h[4];
 } MD5_Core_State;
@@ -176,15 +186,10 @@
 };
 
 struct ssh_kex {
-    /*
-     * Plugging in another KEX algorithm requires structural chaos,
-     * so it's hard to abstract them into nice little structures
-     * like this. Fortunately, all our KEXes are basically
-     * Diffie-Hellman at the moment, so in this structure I simply
-     * parametrise the DH exchange a bit.
-     */
     char *name, *groupname;
-    const unsigned char *pdata, *gdata;/* NULL means use group exchange */
+    enum { KEXTYPE_DH, KEXTYPE_RSA } main_type;
+    /* For DH */
+    const unsigned char *pdata, *gdata; /* NULL means group exchange */
     int plen, glen;
 };
 
Index: putty.h
===================================================================
--- putty.h	(revision 6118)
+++ putty.h	(working copy)
@@ -249,6 +249,7 @@
     KEX_DHGROUP1,
     KEX_DHGROUP14,
     KEX_DHGEX,
+    KEX_RSA,
     KEX_MAX
 };
 
