changeset 836:d7d9f1612d51 ecc

writing out openssh ecc keys works
author Matt Johnston <matt@ucc.asn.au>
date Sun, 20 Oct 2013 21:06:18 +0800
parents 75509065db53
children fdf51d3f26e1
files dropbearconvert.c keyimport.c
diffstat 2 files changed, 263 insertions(+), 201 deletions(-) [+]
line wrap: on
line diff
--- a/dropbearconvert.c	Sat May 25 00:54:19 2013 +0800
+++ b/dropbearconvert.c	Sun Oct 20 21:06:18 2013 +0800
@@ -28,6 +28,8 @@
 #include "buffer.h"
 #include "dbutil.h"
 #include "keyimport.h"
+#include "crypto_desc.h" 
+#include "random.h" 
 
 
 static int do_convert(int intype, const char* infile, int outtype,
@@ -114,7 +116,7 @@
 		const char* outfile) {
 
 	sign_key * key = NULL;
-	char * keytype = NULL;
+	const char * keytype = NULL;
 	int ret = 1;
 
 	key = import_read(infile, NULL, intype);
@@ -124,16 +126,7 @@
 		goto out;
 	}
 
-#ifdef DROPBEAR_RSA
-	if (key->rsakey != NULL) {
-		keytype = "RSA";
-	}
-#endif
-#ifdef DROPBEAR_DSS
-	if (key->dsskey != NULL) {
-		keytype = "DSS";
-	}
-#endif
+	keytype = signkey_name_from_type(key->type, NULL);
 
 	fprintf(stderr, "Key is a %s key\n", keytype);
 
--- a/keyimport.c	Sat May 25 00:54:19 2013 +0800
+++ b/keyimport.c	Sun Oct 20 21:06:18 2013 +0800
@@ -2,8 +2,6 @@
  * Based on PuTTY's import.c for importing/exporting OpenSSH and SSH.com
  * keyfiles.
  *
- * The horribleness of the code is probably mine (matt).
- *
  * Modifications copyright 2003 Matt Johnston
  *
  * PuTTY is copyright 1997-2003 Simon Tatham.
@@ -38,9 +36,9 @@
 #include "dbutil.h"
 #include "ecc.h"
 
-const unsigned char OID_SEC256R1_BLOB[] = {0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07};
-const unsigned char OID_SEC384R1_BLOB[] = {0x2b, 0x81, 0x04, 0x00, 0x22};
-const unsigned char OID_SEC521R1_BLOB[] = {0x2b, 0x81, 0x04, 0x00, 0x23};
+static const unsigned char OID_SEC256R1_BLOB[] = {0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07};
+static const unsigned char OID_SEC384R1_BLOB[] = {0x2b, 0x81, 0x04, 0x00, 0x22};
+static const unsigned char OID_SEC521R1_BLOB[] = {0x2b, 0x81, 0x04, 0x00, 0x23};
 
 #define PUT_32BIT(cp, value) do { \
   (cp)[3] = (unsigned char)(value); \
@@ -130,6 +128,8 @@
 	}
 	buf_free(buf);
 
+	ret->type = type;
+
 	return ret;
 
 error:
@@ -145,23 +145,11 @@
 /* returns 0 on fail, 1 on success */
 static int dropbear_write(const char*filename, sign_key * key) {
 
-	int keytype = -1;
 	buffer * buf;
 	FILE*fp;
 	int len;
 	int ret;
 
-#ifdef DROPBEAR_RSA
-	if (key->rsakey != NULL) {
-		keytype = DROPBEAR_SIGNKEY_RSA;
-	}
-#endif
-#ifdef DROPBEAR_DSS
-	if (key->dsskey != NULL) {
-		keytype = DROPBEAR_SIGNKEY_DSS;
-	}
-#endif
-
 	buf = buf_new(MAX_PRIVKEY_SIZE);
 	buf_put_priv_key(buf, key, key->type);
 
@@ -616,8 +604,10 @@
 
 	if (key->type == OSSH_DSA) {
 		buf_putstring(blobbuf, "ssh-dss", 7);
+		retkey->type = DROPBEAR_SIGNKEY_DSS;
 	} else if (key->type == OSSH_RSA) {
 		buf_putstring(blobbuf, "ssh-rsa", 7);
+		retkey->type = DROPBEAR_SIGNKEY_RSA;
 	}
 
 	for (i = 0; i < num_integers; i++) {
@@ -675,7 +665,6 @@
 
 #ifdef DROPBEAR_ECDSA
 	if (key->type == OSSH_EC) {
-		const char* ecdsa_name;
 		unsigned char* private_key_bytes = NULL;
 		int private_key_len = 0;
 		unsigned char* public_key_bytes = NULL;
@@ -827,202 +816,282 @@
 	char zero[1];
 	int ret = 0;
 	FILE *fp;
-	int keytype = -1;
 
 #ifdef DROPBEAR_RSA
 	mp_int dmp1, dmq1, iqmp, tmpval; /* for rsa */
-
-	if (key->rsakey != NULL) {
-		keytype = DROPBEAR_SIGNKEY_RSA;
-	}
-#endif
-#ifdef DROPBEAR_DSS
-	if (key->dsskey != NULL) {
-		keytype = DROPBEAR_SIGNKEY_DSS;
-	}
 #endif
 
-	dropbear_assert(keytype != -1);
+	if (key->type == DROPBEAR_SIGNKEY_RSA || key->type == DROPBEAR_SIGNKEY_DSS)
+	{
+		/*
+		 * Fetch the key blobs.
+		 */
+		keyblob = buf_new(3000);
+		buf_put_priv_key(keyblob, key, key->type);
+
+		buf_setpos(keyblob, 0);
+		/* skip the "ssh-rsa" or "ssh-dss" header */
+		buf_incrpos(keyblob, buf_getint(keyblob));
+
+		/*
+		 * Find the sequence of integers to be encoded into the OpenSSH
+		 * key blob, and also decide on the header line.
+		 */
+		numbers[0].start = zero; numbers[0].bytes = 1; zero[0] = '\0';
+
+	#ifdef DROPBEAR_RSA
+		if (key->type == DROPBEAR_SIGNKEY_RSA) {
+
+			if (key->rsakey->p == NULL || key->rsakey->q == NULL) {
+				fprintf(stderr, "Pre-0.33 Dropbear keys cannot be converted to OpenSSH keys.\n");
+				goto error;
+			}
 
-	/*
-	 * Fetch the key blobs.
-	 */
-	keyblob = buf_new(3000);
-	buf_put_priv_key(keyblob, key, keytype);
+			/* e */
+			numbers[2].bytes = buf_getint(keyblob);
+			numbers[2].start = buf_getptr(keyblob, numbers[2].bytes);
+			buf_incrpos(keyblob, numbers[2].bytes);
+			
+			/* n */
+			numbers[1].bytes = buf_getint(keyblob);
+			numbers[1].start = buf_getptr(keyblob, numbers[1].bytes);
+			buf_incrpos(keyblob, numbers[1].bytes);
+			
+			/* d */
+			numbers[3].bytes = buf_getint(keyblob);
+			numbers[3].start = buf_getptr(keyblob, numbers[3].bytes);
+			buf_incrpos(keyblob, numbers[3].bytes);
+			
+			/* p */
+			numbers[4].bytes = buf_getint(keyblob);
+			numbers[4].start = buf_getptr(keyblob, numbers[4].bytes);
+			buf_incrpos(keyblob, numbers[4].bytes);
+			
+			/* q */
+			numbers[5].bytes = buf_getint(keyblob);
+			numbers[5].start = buf_getptr(keyblob, numbers[5].bytes);
+			buf_incrpos(keyblob, numbers[5].bytes);
 
-	buf_setpos(keyblob, 0);
-	/* skip the "ssh-rsa" or "ssh-dss" header */
-	buf_incrpos(keyblob, buf_getint(keyblob));
+			/* now calculate some extra parameters: */
+			m_mp_init(&tmpval);
+			m_mp_init(&dmp1);
+			m_mp_init(&dmq1);
+			m_mp_init(&iqmp);
 
-	/*
-	 * Find the sequence of integers to be encoded into the OpenSSH
-	 * key blob, and also decide on the header line.
-	 */
-	numbers[0].start = zero; numbers[0].bytes = 1; zero[0] = '\0';
+			/* dmp1 = d mod (p-1) */
+			if (mp_sub_d(key->rsakey->p, 1, &tmpval) != MP_OKAY) {
+				fprintf(stderr, "Bignum error for p-1\n");
+				goto error;
+			}
+			if (mp_mod(key->rsakey->d, &tmpval, &dmp1) != MP_OKAY) {
+				fprintf(stderr, "Bignum error for dmp1\n");
+				goto error;
+			}
+
+			/* dmq1 = d mod (q-1) */
+			if (mp_sub_d(key->rsakey->q, 1, &tmpval) != MP_OKAY) {
+				fprintf(stderr, "Bignum error for q-1\n");
+				goto error;
+			}
+			if (mp_mod(key->rsakey->d, &tmpval, &dmq1) != MP_OKAY) {
+				fprintf(stderr, "Bignum error for dmq1\n");
+				goto error;
+			}
+
+			/* iqmp = (q^-1) mod p */
+			if (mp_invmod(key->rsakey->q, key->rsakey->p, &iqmp) != MP_OKAY) {
+				fprintf(stderr, "Bignum error for iqmp\n");
+				goto error;
+			}
 
-#ifdef DROPBEAR_RSA
-	if (keytype == DROPBEAR_SIGNKEY_RSA) {
+			extrablob = buf_new(2000);
+			buf_putmpint(extrablob, &dmp1);
+			buf_putmpint(extrablob, &dmq1);
+			buf_putmpint(extrablob, &iqmp);
+			buf_setpos(extrablob, 0);
+			mp_clear(&dmp1);
+			mp_clear(&dmq1);
+			mp_clear(&iqmp);
+			mp_clear(&tmpval);
+			
+			/* dmp1 */
+			numbers[6].bytes = buf_getint(extrablob);
+			numbers[6].start = buf_getptr(extrablob, numbers[6].bytes);
+			buf_incrpos(extrablob, numbers[6].bytes);
+			
+			/* dmq1 */
+			numbers[7].bytes = buf_getint(extrablob);
+			numbers[7].start = buf_getptr(extrablob, numbers[7].bytes);
+			buf_incrpos(extrablob, numbers[7].bytes);
+			
+			/* iqmp */
+			numbers[8].bytes = buf_getint(extrablob);
+			numbers[8].start = buf_getptr(extrablob, numbers[8].bytes);
+			buf_incrpos(extrablob, numbers[8].bytes);
 
-		if (key->rsakey->p == NULL || key->rsakey->q == NULL) {
-			fprintf(stderr, "Pre-0.33 Dropbear keys cannot be converted to OpenSSH keys.\n");
-			goto error;
+			nnumbers = 9;
+			header = "-----BEGIN RSA PRIVATE KEY-----\n";
+			footer = "-----END RSA PRIVATE KEY-----\n";
 		}
+	#endif /* DROPBEAR_RSA */
 
-		/* e */
-		numbers[2].bytes = buf_getint(keyblob);
-		numbers[2].start = buf_getptr(keyblob, numbers[2].bytes);
-		buf_incrpos(keyblob, numbers[2].bytes);
-		
-		/* n */
-		numbers[1].bytes = buf_getint(keyblob);
-		numbers[1].start = buf_getptr(keyblob, numbers[1].bytes);
-		buf_incrpos(keyblob, numbers[1].bytes);
-		
-		/* d */
-		numbers[3].bytes = buf_getint(keyblob);
-		numbers[3].start = buf_getptr(keyblob, numbers[3].bytes);
-		buf_incrpos(keyblob, numbers[3].bytes);
-		
-		/* p */
-		numbers[4].bytes = buf_getint(keyblob);
-		numbers[4].start = buf_getptr(keyblob, numbers[4].bytes);
-		buf_incrpos(keyblob, numbers[4].bytes);
-		
-		/* q */
-		numbers[5].bytes = buf_getint(keyblob);
-		numbers[5].start = buf_getptr(keyblob, numbers[5].bytes);
-		buf_incrpos(keyblob, numbers[5].bytes);
+	#ifdef DROPBEAR_DSS
+		if (key->type == DROPBEAR_SIGNKEY_DSS) {
+
+			/* p */
+			numbers[1].bytes = buf_getint(keyblob);
+			numbers[1].start = buf_getptr(keyblob, numbers[1].bytes);
+			buf_incrpos(keyblob, numbers[1].bytes);
+
+			/* q */
+			numbers[2].bytes = buf_getint(keyblob);
+			numbers[2].start = buf_getptr(keyblob, numbers[2].bytes);
+			buf_incrpos(keyblob, numbers[2].bytes);
+
+			/* g */
+			numbers[3].bytes = buf_getint(keyblob);
+			numbers[3].start = buf_getptr(keyblob, numbers[3].bytes);
+			buf_incrpos(keyblob, numbers[3].bytes);
+
+			/* y */
+			numbers[4].bytes = buf_getint(keyblob);
+			numbers[4].start = buf_getptr(keyblob, numbers[4].bytes);
+			buf_incrpos(keyblob, numbers[4].bytes);
+
+			/* x */
+			numbers[5].bytes = buf_getint(keyblob);
+			numbers[5].start = buf_getptr(keyblob, numbers[5].bytes);
+			buf_incrpos(keyblob, numbers[5].bytes);
+
+			nnumbers = 6;
+			header = "-----BEGIN DSA PRIVATE KEY-----\n";
+			footer = "-----END DSA PRIVATE KEY-----\n";
+		}
+	#endif /* DROPBEAR_DSS */
+
+		/*
+		 * Now count up the total size of the ASN.1 encoded integers,
+		 * so as to determine the length of the containing SEQUENCE.
+		 */
+		len = 0;
+		for (i = 0; i < nnumbers; i++) {
+			len += ber_write_id_len(NULL, 2, numbers[i].bytes, 0);
+			len += numbers[i].bytes;
+		}
+		seqlen = len;
+		/* Now add on the SEQUENCE header. */
+		len += ber_write_id_len(NULL, 16, seqlen, ASN1_CONSTRUCTED);
+		/* Round up to the cipher block size, ensuring we have at least one
+		 * byte of padding (see below). */
+		outlen = len;
+		if (passphrase)
+			outlen = (outlen+8) &~ 7;
+
+		/*
+		 * Now we know how big outblob needs to be. Allocate it.
+		 */
+		outblob = (unsigned char*)m_malloc(outlen);
 
-		/* now calculate some extra parameters: */
-		m_mp_init(&tmpval);
-		m_mp_init(&dmp1);
-		m_mp_init(&dmq1);
-		m_mp_init(&iqmp);
+		/*
+		 * And write the data into it.
+		 */
+		pos = 0;
+		pos += ber_write_id_len(outblob+pos, 16, seqlen, ASN1_CONSTRUCTED);
+		for (i = 0; i < nnumbers; i++) {
+			pos += ber_write_id_len(outblob+pos, 2, numbers[i].bytes, 0);
+			memcpy(outblob+pos, numbers[i].start, numbers[i].bytes);
+			pos += numbers[i].bytes;
+		}
+	} // end RSA and DSS handling
 
-		/* dmp1 = d mod (p-1) */
-		if (mp_sub_d(key->rsakey->p, 1, &tmpval) != MP_OKAY) {
-			fprintf(stderr, "Bignum error for p-1\n");
-			goto error;
-		}
-		if (mp_mod(key->rsakey->d, &tmpval, &dmp1) != MP_OKAY) {
-			fprintf(stderr, "Bignum error for dmp1\n");
-			goto error;
+#ifdef DROPBEAR_ECDSA
+	if (key->type == DROPBEAR_SIGNKEY_ECDSA_NISTP256
+		|| key->type == DROPBEAR_SIGNKEY_ECDSA_NISTP384
+		|| key->type == DROPBEAR_SIGNKEY_ECDSA_NISTP521) {
+
+		/*  SEC1 V2 appendix c.4
+		ECPrivateKey ::= SEQUENCE {
+			version INTEGER { ecPrivkeyVer1(1) } (ecPrivkeyVer1),
+			privateKey OCTET STRING,
+			parameters [0] ECDomainParameters {{ SECGCurveNames }} OPTIONAL, 
+			publicKey [1] BIT STRING OPTIONAL
 		}
+		*/
+		buffer *seq_buf = buf_new(400);
+		ecc_key **eck = signkey_ecc_key_ptr(key, key->type);
+		const unsigned long curve_size = (*eck)->dp->size;
+		int curve_oid_len = 0;
+		const void* curve_oid = NULL;
+		unsigned long pubkey_size = 2*curve_size+1;
 
-		/* dmq1 = d mod (q-1) */
-		if (mp_sub_d(key->rsakey->q, 1, &tmpval) != MP_OKAY) {
-			fprintf(stderr, "Bignum error for q-1\n");
-			goto error;
-		}
-		if (mp_mod(key->rsakey->d, &tmpval, &dmq1) != MP_OKAY) {
-			fprintf(stderr, "Bignum error for dmq1\n");
-			goto error;
-		}
+		/* version. less than 10 bytes */
+		buf_incrwritepos(seq_buf,
+			ber_write_id_len(buf_getwriteptr(seq_buf, 10), 2, 1, 0));
+		buf_putbyte(seq_buf, 1);
+
+		/* privateKey */
+		dropbear_assert(mp_unsigned_bin_size((*eck)->k) == curve_size);
+		buf_incrwritepos(seq_buf,
+			ber_write_id_len(buf_getwriteptr(seq_buf, 10), 4, curve_size, 0));
+	    mp_to_unsigned_bin((*eck)->k, buf_getwriteptr(seq_buf, curve_size));
+		buf_incrwritepos(seq_buf, curve_size);
 
-		/* iqmp = (q^-1) mod p */
-		if (mp_invmod(key->rsakey->q, key->rsakey->p, &iqmp) != MP_OKAY) {
-			fprintf(stderr, "Bignum error for iqmp\n");
-			goto error;
+		/* SECGCurveNames */
+		switch (key->type)
+		{
+			case DROPBEAR_SIGNKEY_ECDSA_NISTP256:
+				curve_oid_len = sizeof(OID_SEC256R1_BLOB);
+				curve_oid = OID_SEC256R1_BLOB;
+				break;
+			case DROPBEAR_SIGNKEY_ECDSA_NISTP384:
+				curve_oid_len = sizeof(OID_SEC384R1_BLOB);
+				curve_oid = OID_SEC384R1_BLOB;
+				break;
+			case DROPBEAR_SIGNKEY_ECDSA_NISTP521:
+				curve_oid_len = sizeof(OID_SEC521R1_BLOB);
+				curve_oid = OID_SEC521R1_BLOB;
+				break;
+			default:
+				dropbear_exit("Internal error");
 		}
 
-		extrablob = buf_new(2000);
-		buf_putmpint(extrablob, &dmp1);
-		buf_putmpint(extrablob, &dmq1);
-		buf_putmpint(extrablob, &iqmp);
-		buf_setpos(extrablob, 0);
-		mp_clear(&dmp1);
-		mp_clear(&dmq1);
-		mp_clear(&iqmp);
-		mp_clear(&tmpval);
-		
-		/* dmp1 */
-		numbers[6].bytes = buf_getint(extrablob);
-		numbers[6].start = buf_getptr(extrablob, numbers[6].bytes);
-		buf_incrpos(extrablob, numbers[6].bytes);
-		
-		/* dmq1 */
-		numbers[7].bytes = buf_getint(extrablob);
-		numbers[7].start = buf_getptr(extrablob, numbers[7].bytes);
-		buf_incrpos(extrablob, numbers[7].bytes);
-		
-		/* iqmp */
-		numbers[8].bytes = buf_getint(extrablob);
-		numbers[8].start = buf_getptr(extrablob, numbers[8].bytes);
-		buf_incrpos(extrablob, numbers[8].bytes);
+		buf_incrwritepos(seq_buf,
+			ber_write_id_len(buf_getwriteptr(seq_buf, 10), 0, 2+curve_oid_len, 0xa0));
+		// object == 6
+		buf_incrwritepos(seq_buf,
+			ber_write_id_len(buf_getwriteptr(seq_buf, 10), 6, curve_oid_len, 0));
+		buf_putbytes(seq_buf, curve_oid, curve_oid_len);
 
-		nnumbers = 9;
-		header = "-----BEGIN RSA PRIVATE KEY-----\n";
-		footer = "-----END RSA PRIVATE KEY-----\n";
-	}
-#endif /* DROPBEAR_RSA */
-
-#ifdef DROPBEAR_DSS
-	if (keytype == DROPBEAR_SIGNKEY_DSS) {
-
-		/* p */
-		numbers[1].bytes = buf_getint(keyblob);
-		numbers[1].start = buf_getptr(keyblob, numbers[1].bytes);
-		buf_incrpos(keyblob, numbers[1].bytes);
-
-		/* q */
-		numbers[2].bytes = buf_getint(keyblob);
-		numbers[2].start = buf_getptr(keyblob, numbers[2].bytes);
-		buf_incrpos(keyblob, numbers[2].bytes);
-
-		/* g */
-		numbers[3].bytes = buf_getint(keyblob);
-		numbers[3].start = buf_getptr(keyblob, numbers[3].bytes);
-		buf_incrpos(keyblob, numbers[3].bytes);
+		buf_incrwritepos(seq_buf,
+			ber_write_id_len(buf_getwriteptr(seq_buf, 10), 1, 2+1+pubkey_size, 0xa0));
+		buf_incrwritepos(seq_buf,
+			ber_write_id_len(buf_getwriteptr(seq_buf, 10), 3, 1+pubkey_size, 0));
+		buf_putbyte(seq_buf, 0);
+		int err = ecc_ansi_x963_export(*eck, buf_getwriteptr(seq_buf, pubkey_size), &pubkey_size);
+		if (err != CRYPT_OK) {
+			dropbear_exit("ECC error");
+		}
+		buf_incrwritepos(seq_buf, pubkey_size);
 
-		/* y */
-		numbers[4].bytes = buf_getint(keyblob);
-		numbers[4].start = buf_getptr(keyblob, numbers[4].bytes);
-		buf_incrpos(keyblob, numbers[4].bytes);
-
-		/* x */
-		numbers[5].bytes = buf_getint(keyblob);
-		numbers[5].start = buf_getptr(keyblob, numbers[5].bytes);
-		buf_incrpos(keyblob, numbers[5].bytes);
+		buf_setpos(seq_buf, 0);
+			
+		outblob = (unsigned char*)m_malloc(200);
 
-		nnumbers = 6;
-		header = "-----BEGIN DSA PRIVATE KEY-----\n";
-		footer = "-----END DSA PRIVATE KEY-----\n";
-	}
-#endif /* DROPBEAR_DSS */
+		pos = 0;
+		pos += ber_write_id_len(outblob+pos, 16, seq_buf->len, ASN1_CONSTRUCTED);
+		memcpy(&outblob[pos], seq_buf->data, seq_buf->len);
+		pos += seq_buf->len;
+		len = pos;
+		outlen = len;
 
-	/*
-	 * Now count up the total size of the ASN.1 encoded integers,
-	 * so as to determine the length of the containing SEQUENCE.
-	 */
-	len = 0;
-	for (i = 0; i < nnumbers; i++) {
-		len += ber_write_id_len(NULL, 2, numbers[i].bytes, 0);
-		len += numbers[i].bytes;
+		buf_burn(seq_buf);
+		buf_free(seq_buf);
+		seq_buf = NULL;
+
+		header = "-----BEGIN EC PRIVATE KEY-----\n";
+		footer = "-----END EC PRIVATE KEY-----\n";
 	}
-	seqlen = len;
-	/* Now add on the SEQUENCE header. */
-	len += ber_write_id_len(NULL, 16, seqlen, ASN1_CONSTRUCTED);
-	/* Round up to the cipher block size, ensuring we have at least one
-	 * byte of padding (see below). */
-	outlen = len;
-	if (passphrase)
-		outlen = (outlen+8) &~ 7;
-
-	/*
-	 * Now we know how big outblob needs to be. Allocate it.
-	 */
-	outblob = (unsigned char*)m_malloc(outlen);
-
-	/*
-	 * And write the data into it.
-	 */
-	pos = 0;
-	pos += ber_write_id_len(outblob+pos, 16, seqlen, ASN1_CONSTRUCTED);
-	for (i = 0; i < nnumbers; i++) {
-		pos += ber_write_id_len(outblob+pos, 2, numbers[i].bytes, 0);
-		memcpy(outblob+pos, numbers[i].start, numbers[i].bytes);
-		pos += numbers[i].bytes;
-	}
+#endif
 
 	/*
 	 * Padding on OpenSSH keys is deterministic. The number of