From 27c6967547013c03024153fbbbbbb42f5f6bd807 Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Sun, 20 Oct 2013 21:06:18 +0800 Subject: writing out openssh ecc keys works --- dropbearconvert.c | 15 +- keyimport.c | 449 +++++++++++++++++++++++++++++++----------------------- 2 files changed, 263 insertions(+), 201 deletions(-) diff --git a/dropbearconvert.c b/dropbearconvert.c index 01fe373..958ced6 100644 --- a/dropbearconvert.c +++ b/dropbearconvert.c @@ -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 @@ static int do_convert(int intype, const char* infile, int outtype, 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 @@ static int do_convert(int intype, const char* infile, int outtype, 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); diff --git a/keyimport.c b/keyimport.c index 05bf800..f513e8a 100644 --- a/keyimport.c +++ b/keyimport.c @@ -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 @@ static sign_key *dropbear_read(const char* filename) { } buf_free(buf); + ret->type = type; + return ret; error: @@ -145,23 +145,11 @@ error: /* 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 @@ static sign_key *openssh_read(const char *filename, char *passphrase) 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 @@ static sign_key *openssh_read(const char *filename, char *passphrase) #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 @@ static int openssh_write(const char *filename, sign_key *key, 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); - /* - * Fetch the key blobs. - */ - keyblob = buf_new(3000); - buf_put_priv_key(keyblob, key, keytype); + buf_setpos(keyblob, 0); + /* skip the "ssh-rsa" or "ssh-dss" header */ + buf_incrpos(keyblob, buf_getint(keyblob)); - 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'; - /* - * 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) { -#ifdef DROPBEAR_RSA - if (keytype == 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; + } - 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; - } + /* 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); + + /* now calculate some extra parameters: */ + m_mp_init(&tmpval); + m_mp_init(&dmp1); + m_mp_init(&dmq1); + m_mp_init(&iqmp); + + /* 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; + } - /* 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); - - /* now calculate some extra parameters: */ - m_mp_init(&tmpval); - m_mp_init(&dmp1); - m_mp_init(&dmq1); - m_mp_init(&iqmp); - - /* 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; + /* 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; + } + + 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); + + nnumbers = 9; + header = "-----BEGIN RSA PRIVATE KEY-----\n"; + footer = "-----END RSA PRIVATE KEY-----\n"; } - if (mp_mod(key->rsakey->d, &tmpval, &dmp1) != MP_OKAY) { - fprintf(stderr, "Bignum error for dmp1\n"); - goto error; + #endif /* DROPBEAR_RSA */ + + #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 */ - /* 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; + /* + * 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; } - if (mp_mod(key->rsakey->d, &tmpval, &dmq1) != MP_OKAY) { - fprintf(stderr, "Bignum error for dmq1\n"); - goto error; + 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; } + } // end RSA and DSS handling - /* 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_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; + + /* 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); + + /* 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); - - 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); - - /* 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_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); + + 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); - nnumbers = 6; - header = "-----BEGIN DSA PRIVATE KEY-----\n"; - footer = "-----END DSA PRIVATE KEY-----\n"; - } -#endif /* DROPBEAR_DSS */ + buf_setpos(seq_buf, 0); + + outblob = (unsigned char*)m_malloc(200); - /* - * 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; + 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 we know how big outblob needs to be. Allocate it. - */ - outblob = (unsigned char*)m_malloc(outlen); + buf_burn(seq_buf); + buf_free(seq_buf); + seq_buf = NULL; - /* - * 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; + header = "-----BEGIN EC PRIVATE KEY-----\n"; + footer = "-----END EC PRIVATE KEY-----\n"; } +#endif /* * Padding on OpenSSH keys is deterministic. The number of -- cgit v1.2.1