- (djm) Merge OpenBSD changes:
   - markus@cvs.openbsd.org  2000/11/06 16:04:56
     [channels.c channels.h clientloop.c nchan.c serverloop.c]
     [session.c ssh.c]
     agent forwarding and -R for ssh2, based on work from
     jhuuskon@messi.uku.fi
   - markus@cvs.openbsd.org  2000/11/06 16:13:27
     [ssh.c sshconnect.c sshd.c]
     do not disabled rhosts(rsa) if server port > 1024; from
     pekkas@netcore.fi
   - markus@cvs.openbsd.org  2000/11/06 16:16:35
     [sshconnect.c]
     downgrade client to 1.3 if server is 1.4; help from mdb@juniper.net
   - markus@cvs.openbsd.org  2000/11/09 18:04:40
     [auth1.c]
     typo; from mouring@pconline.com
   - markus@cvs.openbsd.org  2000/11/12 12:03:28
     [ssh-agent.c]
     off-by-one when removing a key from the agent
   - markus@cvs.openbsd.org  2000/11/12 12:50:39
     [auth-rh-rsa.c auth2.c authfd.c authfd.h]
     [authfile.c hostfile.c kex.c kex.h key.c key.h myproposal.h]
     [readconf.c readconf.h rsa.c rsa.h servconf.c servconf.h ssh-add.c]
     [ssh-agent.c ssh-keygen.1 ssh-keygen.c ssh.1 ssh.c ssh_config]
     [sshconnect1.c sshconnect2.c sshd.8 sshd.c sshd_config ssh-dss.c]
     [ssh-dss.h ssh-rsa.c ssh-rsa.h dsa.c dsa.h]
     add support for RSA to SSH2.  please test.
     there are now 3 types of keys: RSA1 is used by ssh-1 only,
     RSA and DSA are used by SSH2.
     you can use 'ssh-keygen -t rsa -f ssh2_rsa_file' to generate RSA
     keys for SSH2 and use the RSA keys for hostkeys or for user keys.
     SSH2 RSA or DSA keys are added to .ssh/authorised_keys2 as before.
 - (djm) Fix up Makefile and Redhat init script to create RSA host keys
 - (djm) Change to interim version
diff --git a/key.c b/key.c
index f7df0bb..a6b25b7 100644
--- a/key.c
+++ b/key.c
@@ -39,12 +39,14 @@
 #include <openssl/evp.h>
 #include "xmalloc.h"
 #include "key.h"
-#include "dsa.h"
+#include "rsa.h"
+#include "ssh-dss.h"
+#include "ssh-rsa.h"
 #include "uuencode.h"
+#include "buffer.h"
+#include "bufaux.h"
 
-RCSID("$OpenBSD: key.c,v 1.11 2000/09/07 20:27:51 deraadt Exp $");
-
-#define SSH_DSS "ssh-dss"
+RCSID("$OpenBSD: key.c,v 1.12 2000/11/12 19:50:37 markus Exp $");
 
 Key *
 key_new(int type)
@@ -57,6 +59,7 @@
 	k->dsa = NULL;
 	k->rsa = NULL;
 	switch (k->type) {
+	case KEY_RSA1:
 	case KEY_RSA:
 		rsa = RSA_new();
 		rsa->n = BN_new();
@@ -71,7 +74,7 @@
 		dsa->pub_key = BN_new();
 		k->dsa = dsa;
 		break;
-	case KEY_EMPTY:
+	case KEY_UNSPEC:
 		break;
 	default:
 		fatal("key_new: bad key type %d", k->type);
@@ -79,10 +82,35 @@
 	}
 	return k;
 }
+Key *
+key_new_private(int type)
+{
+	Key *k = key_new(type);
+	switch (k->type) {
+	case KEY_RSA1:
+	case KEY_RSA:
+		k->rsa->d = BN_new();
+		k->rsa->iqmp = BN_new();
+		k->rsa->q = BN_new();
+		k->rsa->p = BN_new();
+		k->rsa->dmq1 = BN_new();
+		k->rsa->dmp1 = BN_new();
+		break;
+	case KEY_DSA:
+		k->dsa->priv_key = BN_new();
+		break;
+	case KEY_UNSPEC:
+		break;
+	default:
+		break;
+	}
+	return k;
+}
 void
 key_free(Key *k)
 {
 	switch (k->type) {
+	case KEY_RSA1:
 	case KEY_RSA:
 		if (k->rsa != NULL)
 			RSA_free(k->rsa);
@@ -93,6 +121,8 @@
 			DSA_free(k->dsa);
 		k->dsa = NULL;
 		break;
+	case KEY_UNSPEC:
+		break;
 	default:
 		fatal("key_free: bad key type %d", k->type);
 		break;
@@ -105,6 +135,7 @@
 	if (a == NULL || b == NULL || a->type != b->type)
 		return 0;
 	switch (a->type) {
+	case KEY_RSA1:
 	case KEY_RSA:
 		return a->rsa != NULL && b->rsa != NULL &&
 		    BN_cmp(a->rsa->e, b->rsa->e) == 0 &&
@@ -136,8 +167,9 @@
 	int len = 0;
 	int nlen, elen;
 
+	retval[0] = '\0';
 	switch (k->type) {
-	case KEY_RSA:
+	case KEY_RSA1:
 		nlen = BN_num_bytes(k->rsa->n);
 		elen = BN_num_bytes(k->rsa->e);
 		len = nlen + elen;
@@ -146,14 +178,16 @@
 		BN_bn2bin(k->rsa->e, blob + nlen);
 		break;
 	case KEY_DSA:
-		dsa_make_key_blob(k, &blob, &len);
+	case KEY_RSA:
+		key_to_blob(k, &blob, &len);
+		break;
+	case KEY_UNSPEC:
+		return retval;
 		break;
 	default:
 		fatal("key_fingerprint: bad key type %d", k->type);
 		break;
 	}
-	retval[0] = '\0';
-
 	if (blob != NULL) {
 		int i;
 		unsigned char digest[EVP_MAX_MD_SIZE];
@@ -229,56 +263,106 @@
 	free(buf);
 	return 1;
 }
-unsigned int
+
+/* returns 1 ok, -1 error, 0 type mismatch */
+int
 key_read(Key *ret, char **cpp)
 {
 	Key *k;
-	unsigned int bits = 0;
-	char *cp;
-	int len, n;
+	int success = -1;
+	char *cp, *space;
+	int len, n, type;
+	u_int bits;
 	unsigned char *blob;
 
 	cp = *cpp;
 
 	switch(ret->type) {
-	case KEY_RSA:
+	case KEY_RSA1:
 		/* Get number of bits. */
 		if (*cp < '0' || *cp > '9')
-			return 0;	/* Bad bit count... */
+			return -1;	/* Bad bit count... */
 		for (bits = 0; *cp >= '0' && *cp <= '9'; cp++)
 			bits = 10 * bits + *cp - '0';
 		if (bits == 0)
-			return 0;
+			return -1;
 		*cpp = cp;
 		/* Get public exponent, public modulus. */
 		if (!read_bignum(cpp, ret->rsa->e))
-			return 0;
+			return -1;
 		if (!read_bignum(cpp, ret->rsa->n))
-			return 0;
+			return -1;
+		success = 1;
 		break;
+	case KEY_UNSPEC:
+	case KEY_RSA:
 	case KEY_DSA:
-		if (strncmp(cp, SSH_DSS " ", 7) != 0)
+		space = strchr(cp, ' ');
+		if (space == NULL) {
+			debug3("key_read: no space");
+			return -1;
+		}
+		*space = '\0';
+		type = key_type_from_name(cp);
+		*space = ' ';
+		if (type == KEY_UNSPEC) {
+			debug3("key_read: no key found");
+			return -1;
+		}
+		cp = space+1;
+		if (*cp == '\0') {
+			debug3("key_read: short string");
+			return -1;
+		}
+		if (ret->type == KEY_UNSPEC) {
+			ret->type = type;
+		} else if (ret->type != type) {
+			/* is a key, but different type */
+			debug3("key_read: type mismatch");
 			return 0;
-		cp += 7;
+		}
 		len = 2*strlen(cp);
 		blob = xmalloc(len);
 		n = uudecode(cp, blob, len);
 		if (n < 0) {
 			error("key_read: uudecode %s failed", cp);
-			return 0;
+			return -1;
 		}
-		k = dsa_key_from_blob(blob, n);
+		k = key_from_blob(blob, n);
 		if (k == NULL) {
-			error("key_read: dsa_key_from_blob %s failed", cp);
-			return 0;
+			error("key_read: key_from_blob %s failed", cp);
+			return -1;
 		}
 		xfree(blob);
-		if (ret->dsa != NULL)
-			DSA_free(ret->dsa);
-		ret->dsa = k->dsa;
-		k->dsa = NULL;
+		if (k->type != type) {
+			error("key_read: type mismatch: encoding error");
+			key_free(k);
+			return -1;
+		}
+/*XXXX*/
+		if (ret->type == KEY_RSA) {
+			if (ret->rsa != NULL)
+				RSA_free(ret->rsa);
+			ret->rsa = k->rsa;
+			k->rsa = NULL;
+			success = 1;
+#ifdef DEBUG_PK
+			RSA_print_fp(stderr, ret->rsa, 8);
+#endif
+		} else {
+			if (ret->dsa != NULL)
+				DSA_free(ret->dsa);
+			ret->dsa = k->dsa;
+			k->dsa = NULL;
+			success = 1;
+#ifdef DEBUG_PK
+			DSA_print_fp(stderr, ret->dsa, 8);
+#endif
+		}
+/*XXXX*/
+		if (success != 1)
+			break;
 		key_free(k);
-		bits = BN_num_bits(ret->dsa->p);
 		/* advance cp: skip whitespace and data */
 		while (*cp == ' ' || *cp == '\t')
 			cp++;
@@ -290,7 +374,7 @@
 		fatal("key_read: bad key type: %d", ret->type);
 		break;
 	}
-	return bits;
+	return success;
 }
 int
 key_write(Key *key, FILE *f)
@@ -298,7 +382,7 @@
 	int success = 0;
 	unsigned int bits = 0;
 
-	if (key->type == KEY_RSA && key->rsa != NULL) {
+	if (key->type == KEY_RSA1 && key->rsa != NULL) {
 		/* size of modulus 'n' */
 		bits = BN_num_bits(key->rsa->n);
 		fprintf(f, "%u", bits);
@@ -308,14 +392,15 @@
 		} else {
 			error("key_write: failed for RSA key");
 		}
-	} else if (key->type == KEY_DSA && key->dsa != NULL) {
+	} else if ((key->type == KEY_DSA && key->dsa != NULL) ||
+	    (key->type == KEY_RSA && key->rsa != NULL)) {
 		int len, n;
 		unsigned char *blob, *uu;
-		dsa_make_key_blob(key, &blob, &len);
+		key_to_blob(key, &blob, &len);
 		uu = xmalloc(2*len);
 		n = uuencode(blob, len, uu, 2*len);
 		if (n > 0) {
-			fprintf(f, "%s %s", SSH_DSS, uu);
+			fprintf(f, "%s %s", key_ssh_name(key), uu);
 			success = 1;
 		}
 		xfree(blob);
@@ -327,6 +412,9 @@
 key_type(Key *k)
 {
 	switch (k->type) {
+	case KEY_RSA1:
+		return "RSA1";
+		break;
 	case KEY_RSA:
 		return "RSA";
 		break;
@@ -336,9 +424,23 @@
 	}
 	return "unknown";
 }
-unsigned int
+char *
+key_ssh_name(Key *k)
+{
+	switch (k->type) {
+	case KEY_RSA:
+		return "ssh-rsa";
+		break;
+	case KEY_DSA:
+		return "ssh-dss";
+		break;
+	}
+	return "ssh-unknown";
+}
+u_int
 key_size(Key *k){
 	switch (k->type) {
+	case KEY_RSA1:
 	case KEY_RSA:
 		return BN_num_bits(k->rsa->n);
 		break;
@@ -348,3 +450,219 @@
 	}
 	return 0;
 }
+
+RSA *
+rsa_generate_private_key(unsigned int bits)
+{
+        RSA *private;
+        private = RSA_generate_key(bits, 35, NULL, NULL);
+        if (private == NULL)
+                fatal("rsa_generate_private_key: key generation failed.");
+        return private;
+}
+
+DSA*
+dsa_generate_private_key(unsigned int bits)
+{
+	DSA *private = DSA_generate_parameters(bits, NULL, 0, NULL, NULL, NULL, NULL);
+	if (private == NULL)
+		fatal("dsa_generate_private_key: DSA_generate_parameters failed");
+	if (!DSA_generate_key(private))
+                fatal("dsa_generate_private_key: DSA_generate_key failed.");
+        if (private == NULL)
+                fatal("dsa_generate_private_key: NULL.");
+	return private;
+}
+
+Key *
+key_generate(int type, unsigned int bits)
+{
+	Key *k = key_new(KEY_UNSPEC);
+	switch (type) {
+        case KEY_DSA:
+		k->dsa = dsa_generate_private_key(bits);
+		break;
+	case KEY_RSA:
+	case KEY_RSA1:
+		k->rsa = rsa_generate_private_key(bits);
+		break;
+	default:
+                fatal("key_generate: unknown type %d", type);
+	}
+        k->type = type;
+	return k;
+}
+
+Key *
+key_from_private(Key *k)
+{
+	Key *n = NULL;
+	switch (k->type) {
+        case KEY_DSA:
+		n = key_new(k->type);
+		BN_copy(n->dsa->p, k->dsa->p);
+		BN_copy(n->dsa->q, k->dsa->q);
+		BN_copy(n->dsa->g, k->dsa->g);
+		BN_copy(n->dsa->pub_key, k->dsa->pub_key);
+		break;
+	case KEY_RSA:
+	case KEY_RSA1:
+		n = key_new(k->type);
+		BN_copy(n->rsa->n, k->rsa->n);
+		BN_copy(n->rsa->e, k->rsa->e);
+		break;
+	default:
+                fatal("key_from_private: unknown type %d", k->type);
+		break;
+	}
+	return n;
+}
+
+int
+key_type_from_name(char *name)
+{
+	if (strcmp(name, "rsa1") == 0){
+		return KEY_RSA1;
+	} else if (strcmp(name, "rsa") == 0){
+		return KEY_RSA;
+	} else if (strcmp(name, "dsa") == 0){
+		return KEY_DSA;
+	} else if (strcmp(name, "ssh-rsa") == 0){
+		return KEY_RSA;
+	} else if (strcmp(name, "ssh-dss") == 0){
+		return KEY_DSA;
+	}
+	debug("key_type_from_name: unknown key type '%s'", name);
+	return KEY_UNSPEC;
+}
+
+Key *
+key_from_blob(char *blob, int blen)
+{
+	Buffer b;
+	char *ktype;
+	int rlen, type;
+	Key *key = NULL;
+
+#ifdef DEBUG_PK
+	dump_base64(stderr, blob, blen);
+#endif
+	buffer_init(&b);
+	buffer_append(&b, blob, blen);
+	ktype = buffer_get_string(&b, NULL);
+	type = key_type_from_name(ktype);
+
+	switch(type){
+	case KEY_RSA:
+		key = key_new(type);
+		buffer_get_bignum2(&b, key->rsa->n);
+		buffer_get_bignum2(&b, key->rsa->e);
+#ifdef DEBUG_PK
+		RSA_print_fp(stderr, key->rsa, 8);
+#endif
+		break;
+	case KEY_DSA:
+		key = key_new(type);
+		buffer_get_bignum2(&b, key->dsa->p);
+		buffer_get_bignum2(&b, key->dsa->q);
+		buffer_get_bignum2(&b, key->dsa->g);
+		buffer_get_bignum2(&b, key->dsa->pub_key);
+#ifdef DEBUG_PK
+		DSA_print_fp(stderr, key->dsa, 8);
+#endif
+		break;
+	case KEY_UNSPEC:
+		key = key_new(type);
+		break;
+	default:
+		error("key_from_blob: cannot handle type %s", ktype);
+		break;
+	}
+	rlen = buffer_len(&b);
+	if (key != NULL && rlen != 0)
+		error("key_from_blob: remaining bytes in key blob %d", rlen);
+	xfree(ktype);
+	buffer_free(&b);
+	return key;
+}
+
+int
+key_to_blob(Key *key, unsigned char **blobp, unsigned int *lenp)
+{
+	Buffer b;
+	int len;
+	unsigned char *buf;
+
+	if (key == NULL) {
+		error("key_to_blob: key == NULL");
+		return 0;
+	}
+	buffer_init(&b);
+	switch(key->type){
+	case KEY_DSA:
+		buffer_put_cstring(&b, key_ssh_name(key));
+		buffer_put_bignum2(&b, key->dsa->p);
+		buffer_put_bignum2(&b, key->dsa->q);
+		buffer_put_bignum2(&b, key->dsa->g);
+		buffer_put_bignum2(&b, key->dsa->pub_key);
+		break;
+	case KEY_RSA:
+		buffer_put_cstring(&b, key_ssh_name(key));
+		buffer_put_bignum2(&b, key->rsa->n);
+		buffer_put_bignum2(&b, key->rsa->e);
+		break;
+	default:
+		error("key_to_blob: illegal key type %d", key->type);
+		break;
+	}
+	len = buffer_len(&b);
+	buf = xmalloc(len);
+	memcpy(buf, buffer_ptr(&b), len);
+	memset(buffer_ptr(&b), 0, len);
+	buffer_free(&b);
+	if (lenp != NULL)
+		*lenp = len;
+	if (blobp != NULL)
+		*blobp = buf;
+	return len;
+}
+
+int
+key_sign(
+    Key *key,
+    unsigned char **sigp, int *lenp,
+    unsigned char *data, int datalen)
+{
+	switch(key->type){
+	case KEY_DSA:
+		return ssh_dss_sign(key, sigp, lenp, data, datalen);
+		break;
+	case KEY_RSA:
+		return ssh_rsa_sign(key, sigp, lenp, data, datalen);
+		break;
+	default:
+		error("key_sign: illegal key type %d", key->type);
+		return -1;
+		break;
+	}
+}
+
+int
+key_verify(
+    Key *key,
+    unsigned char *signature, int signaturelen,
+    unsigned char *data, int datalen)
+{
+	switch(key->type){
+	case KEY_DSA:
+		return ssh_dss_verify(key, signature, signaturelen, data, datalen);
+		break;
+	case KEY_RSA:
+		return ssh_rsa_verify(key, signature, signaturelen, data, datalen);
+		break;
+	default:
+		error("key_verify: illegal key type %d", key->type);
+		return -1;
+		break;
+	}
+}