Mercurial > dropbear
comparison keyimport.c @ 1659:d32bcb5c557d
Add Ed25519 support (#91)
* Add support for Ed25519 as a public key type
Ed25519 is a elliptic curve signature scheme that offers
better security than ECDSA and DSA and good performance. It may be
used for both user and host keys.
OpenSSH key import and fuzzer are not supported yet.
Initially inspired by Peter Szabo.
* Add curve25519 and ed25519 fuzzers
* Add import and export of Ed25519 keys
author | Vladislav Grishenko <themiron@users.noreply.github.com> |
---|---|
date | Wed, 11 Mar 2020 21:09:45 +0500 |
parents | 315fcba6960e |
children | ba6fc7afe1c5 |
comparison
equal
deleted
inserted
replaced
1658:7402218141d4 | 1659:d32bcb5c557d |
---|---|
33 #include "keyimport.h" | 33 #include "keyimport.h" |
34 #include "bignum.h" | 34 #include "bignum.h" |
35 #include "buffer.h" | 35 #include "buffer.h" |
36 #include "dbutil.h" | 36 #include "dbutil.h" |
37 #include "ecc.h" | 37 #include "ecc.h" |
38 #include "ssh.h" | |
39 | |
40 static const unsigned char OSSH_PKEY_BLOB[] = | |
41 "openssh-key-v1\0" /* AUTH_MAGIC */ | |
42 "\0\0\0\4none" /* cipher name*/ | |
43 "\0\0\0\4none" /* kdf name */ | |
44 "\0\0\0\0" /* kdf */ | |
45 "\0\0\0\1"; /* key num */ | |
46 #define OSSH_PKEY_BLOBLEN (sizeof(OSSH_PKEY_BLOB) - 1) | |
38 | 47 |
39 #if DROPBEAR_ECDSA | 48 #if DROPBEAR_ECDSA |
40 static const unsigned char OID_SEC256R1_BLOB[] = {0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07}; | 49 static const unsigned char OID_SEC256R1_BLOB[] = {0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07}; |
41 static const unsigned char OID_SEC384R1_BLOB[] = {0x2b, 0x81, 0x04, 0x00, 0x22}; | 50 static const unsigned char OID_SEC384R1_BLOB[] = {0x2b, 0x81, 0x04, 0x00, 0x22}; |
42 static const unsigned char OID_SEC521R1_BLOB[] = {0x2b, 0x81, 0x04, 0x00, 0x23}; | 51 static const unsigned char OID_SEC521R1_BLOB[] = {0x2b, 0x81, 0x04, 0x00, 0x23}; |
350 | 359 |
351 /* ---------------------------------------------------------------------- | 360 /* ---------------------------------------------------------------------- |
352 * Code to read and write OpenSSH private keys. | 361 * Code to read and write OpenSSH private keys. |
353 */ | 362 */ |
354 | 363 |
355 enum { OSSH_DSA, OSSH_RSA, OSSH_EC }; | 364 enum { OSSH_DSA, OSSH_RSA, OSSH_EC, OSSH_PKEY }; |
356 struct openssh_key { | 365 struct openssh_key { |
357 int type; | 366 int type; |
358 int encrypted; | 367 int encrypted; |
359 char iv[32]; | 368 char iv[32]; |
360 unsigned char *keyblob; | 369 unsigned char *keyblob; |
362 }; | 371 }; |
363 | 372 |
364 static struct openssh_key *load_openssh_key(const char *filename) | 373 static struct openssh_key *load_openssh_key(const char *filename) |
365 { | 374 { |
366 struct openssh_key *ret; | 375 struct openssh_key *ret; |
376 buffer *buf = NULL; | |
367 FILE *fp = NULL; | 377 FILE *fp = NULL; |
368 char buffer[256]; | 378 char buffer[256]; |
369 char *errmsg = NULL, *p = NULL; | 379 char *errmsg = NULL, *p = NULL; |
370 int headers_done; | 380 int headers_done; |
371 unsigned long len, outlen; | 381 unsigned long len; |
372 | 382 |
373 ret = (struct openssh_key*)m_malloc(sizeof(struct openssh_key)); | 383 ret = (struct openssh_key*)m_malloc(sizeof(struct openssh_key)); |
374 ret->keyblob = NULL; | 384 ret->keyblob = NULL; |
375 ret->keyblob_len = ret->keyblob_size = 0; | 385 ret->keyblob_len = ret->keyblob_size = 0; |
376 ret->encrypted = 0; | 386 ret->encrypted = 0; |
395 ret->type = OSSH_RSA; | 405 ret->type = OSSH_RSA; |
396 else if (!strcmp(buffer, "-----BEGIN DSA PRIVATE KEY-----\n")) | 406 else if (!strcmp(buffer, "-----BEGIN DSA PRIVATE KEY-----\n")) |
397 ret->type = OSSH_DSA; | 407 ret->type = OSSH_DSA; |
398 else if (!strcmp(buffer, "-----BEGIN EC PRIVATE KEY-----\n")) | 408 else if (!strcmp(buffer, "-----BEGIN EC PRIVATE KEY-----\n")) |
399 ret->type = OSSH_EC; | 409 ret->type = OSSH_EC; |
410 else if (!strcmp(buffer, "-----BEGIN OPENSSH PRIVATE KEY-----\n")) | |
411 ret->type = OSSH_PKEY; | |
400 else { | 412 else { |
401 errmsg = "Unrecognised key type"; | 413 errmsg = "Unrecognised key type"; |
402 goto error; | 414 goto error; |
403 } | 415 } |
404 | 416 |
405 headers_done = 0; | 417 headers_done = 0; |
418 buf = buf_new(0); | |
406 while (1) { | 419 while (1) { |
407 if (!fgets(buffer, sizeof(buffer), fp)) { | 420 if (!fgets(buffer, sizeof(buffer), fp)) { |
408 errmsg = "Unexpected end of file"; | 421 errmsg = "Unexpected end of file"; |
409 goto error; | 422 goto error; |
410 } | 423 } |
446 } | 459 } |
447 } | 460 } |
448 } else { | 461 } else { |
449 headers_done = 1; | 462 headers_done = 1; |
450 len = strlen(buffer); | 463 len = strlen(buffer); |
451 outlen = len*4/3; | 464 buf = buf_resize(buf, buf->size + len); |
452 if (ret->keyblob_len + outlen > ret->keyblob_size) { | 465 buf_putbytes(buf, buffer, len); |
453 ret->keyblob_size = ret->keyblob_len + outlen + 256; | 466 } |
454 ret->keyblob = (unsigned char*)m_realloc(ret->keyblob, | 467 } |
455 ret->keyblob_size); | 468 |
456 } | 469 if (buf && buf->len) { |
457 outlen = ret->keyblob_size - ret->keyblob_len; | 470 ret->keyblob_size = ret->keyblob_len + buf->len*4/3 + 256; |
458 if (base64_decode((const unsigned char *)buffer, len, | 471 ret->keyblob = (unsigned char*)m_realloc(ret->keyblob, ret->keyblob_size); |
459 ret->keyblob + ret->keyblob_len, &outlen) != CRYPT_OK){ | 472 len = ret->keyblob_size; |
460 errmsg = "Error decoding base64"; | 473 if (base64_decode((const unsigned char *)buf->data, buf->len, |
461 goto error; | 474 ret->keyblob, &len) != CRYPT_OK){ |
462 } | 475 errmsg = "Error decoding base64"; |
463 ret->keyblob_len += outlen; | 476 goto error; |
464 } | 477 } |
478 ret->keyblob_len = len; | |
479 } | |
480 | |
481 if (ret->type == OSSH_PKEY) { | |
482 if (ret->keyblob_len < OSSH_PKEY_BLOBLEN || | |
483 memcmp(ret->keyblob, OSSH_PKEY_BLOB, OSSH_PKEY_BLOBLEN)) { | |
484 errmsg = "Error decoding OpenSSH key"; | |
485 goto error; | |
486 } | |
487 ret->keyblob_len -= OSSH_PKEY_BLOBLEN; | |
488 memmove(ret->keyblob, ret->keyblob + OSSH_PKEY_BLOBLEN, ret->keyblob_len); | |
465 } | 489 } |
466 | 490 |
467 if (ret->keyblob_len == 0 || !ret->keyblob) { | 491 if (ret->keyblob_len == 0 || !ret->keyblob) { |
468 errmsg = "Key body not present"; | 492 errmsg = "Key body not present"; |
469 goto error; | 493 goto error; |
472 if (ret->encrypted && ret->keyblob_len % 8 != 0) { | 496 if (ret->encrypted && ret->keyblob_len % 8 != 0) { |
473 errmsg = "Encrypted key blob is not a multiple of cipher block size"; | 497 errmsg = "Encrypted key blob is not a multiple of cipher block size"; |
474 goto error; | 498 goto error; |
475 } | 499 } |
476 | 500 |
501 if (buf) { | |
502 buf_burn(buf); | |
503 buf_free(buf); | |
504 } | |
477 m_burn(buffer, sizeof(buffer)); | 505 m_burn(buffer, sizeof(buffer)); |
478 return ret; | 506 return ret; |
479 | 507 |
480 error: | 508 error: |
509 if (buf) { | |
510 buf_burn(buf); | |
511 buf_free(buf); | |
512 } | |
481 m_burn(buffer, sizeof(buffer)); | 513 m_burn(buffer, sizeof(buffer)); |
482 if (ret) { | 514 if (ret) { |
483 if (ret->keyblob) { | 515 if (ret->keyblob) { |
484 m_burn(ret->keyblob, ret->keyblob_size); | 516 m_burn(ret->keyblob, ret->keyblob_size); |
485 m_free(ret->keyblob); | 517 m_free(ret->keyblob); |
565 key->keyblob, key->keyblob_len); | 597 key->keyblob, key->keyblob_len); |
566 | 598 |
567 memset(&md5c, 0, sizeof(md5c)); | 599 memset(&md5c, 0, sizeof(md5c)); |
568 memset(keybuf, 0, sizeof(keybuf)); | 600 memset(keybuf, 0, sizeof(keybuf)); |
569 #endif | 601 #endif |
602 } | |
603 | |
604 /* | |
605 * Now we have a decrypted key blob, which contains OpenSSH | |
606 * encoded private key. We must now untangle the OpenSSH format. | |
607 */ | |
608 if (key->type == OSSH_PKEY) { | |
609 blobbuf = buf_new(key->keyblob_len); | |
610 buf_putbytes(blobbuf, key->keyblob, key->keyblob_len); | |
611 buf_setpos(blobbuf, 0); | |
612 | |
613 /* limit length of private key blob */ | |
614 len = buf_getint(blobbuf); | |
615 buf_setlen(blobbuf, blobbuf->pos + len); | |
616 | |
617 type = DROPBEAR_SIGNKEY_ANY; | |
618 if (buf_get_pub_key(blobbuf, retkey, &type) | |
619 != DROPBEAR_SUCCESS) { | |
620 errmsg = "Error parsing OpenSSH key"; | |
621 goto ossh_error; | |
622 } | |
623 | |
624 /* restore full length */ | |
625 buf_setlen(blobbuf, key->keyblob_len); | |
626 | |
627 if (type != DROPBEAR_SIGNKEY_NONE) { | |
628 retkey->type = type; | |
629 /* limit length of private key blob */ | |
630 len = buf_getint(blobbuf); | |
631 buf_setlen(blobbuf, blobbuf->pos + len); | |
632 #if DROPBEAR_ED25519 | |
633 if (type == DROPBEAR_SIGNKEY_ED25519) { | |
634 buf_incrpos(blobbuf, 8); | |
635 buf_eatstring(blobbuf); | |
636 buf_eatstring(blobbuf); | |
637 buf_incrpos(blobbuf, -SSH_SIGNKEY_ED25519_LEN-4); | |
638 if (buf_get_ed25519_priv_key(blobbuf, retkey->ed25519key) | |
639 == DROPBEAR_SUCCESS) { | |
640 errmsg = NULL; | |
641 retval = retkey; | |
642 goto error; | |
643 } | |
644 } | |
645 #endif | |
646 } | |
647 | |
648 errmsg = "Unsupported OpenSSH key type"; | |
649 ossh_error: | |
650 sign_key_free(retkey); | |
651 retkey = NULL; | |
652 goto error; | |
570 } | 653 } |
571 | 654 |
572 /* | 655 /* |
573 * Now we have a decrypted key blob, which contains an ASN.1 | 656 * Now we have a decrypted key blob, which contains an ASN.1 |
574 * encoded private key. We must now untangle the ASN.1. | 657 * encoded private key. We must now untangle the ASN.1. |
1124 buf_free(seq_buf); | 1207 buf_free(seq_buf); |
1125 seq_buf = NULL; | 1208 seq_buf = NULL; |
1126 | 1209 |
1127 header = "-----BEGIN EC PRIVATE KEY-----\n"; | 1210 header = "-----BEGIN EC PRIVATE KEY-----\n"; |
1128 footer = "-----END EC PRIVATE KEY-----\n"; | 1211 footer = "-----END EC PRIVATE KEY-----\n"; |
1212 } | |
1213 #endif | |
1214 | |
1215 #if DROPBEAR_ED25519 | |
1216 if (key->type == DROPBEAR_SIGNKEY_ED25519) { | |
1217 buffer *buf = buf_new(300); | |
1218 keyblob = buf_new(100); | |
1219 extrablob = buf_new(200); | |
1220 | |
1221 /* private key blob w/o header */ | |
1222 buf_put_priv_key(keyblob, key, key->type); | |
1223 buf_setpos(keyblob, 0); | |
1224 buf_incrpos(keyblob, buf_getint(keyblob)); | |
1225 len = buf_getint(keyblob); | |
1226 | |
1227 /* header */ | |
1228 buf_putbytes(buf, OSSH_PKEY_BLOB, OSSH_PKEY_BLOBLEN); | |
1229 | |
1230 /* public key */ | |
1231 buf_put_pub_key(buf, key, key->type); | |
1232 | |
1233 /* private key */ | |
1234 buf_incrwritepos(extrablob, 4); | |
1235 buf_put_pub_key(extrablob, key, key->type); | |
1236 buf_putstring(extrablob, buf_getptr(keyblob, len), len); | |
1237 /* comment */ | |
1238 buf_putstring(extrablob, "", 0); | |
1239 /* padding to cipher block length */ | |
1240 len = (extrablob->len+8) & ~7; | |
1241 for (i = 1; len - extrablob->len > 0; i++) | |
1242 buf_putbyte(extrablob, i); | |
1243 buf_setpos(extrablob, 0); | |
1244 buf_putbytes(extrablob, "\0\0\0\0\0\0\0\0", 8); | |
1245 buf_putbufstring(buf, extrablob); | |
1246 | |
1247 outlen = len = pos = buf->len; | |
1248 outblob = (unsigned char*)m_malloc(outlen); | |
1249 memcpy(outblob, buf->data, buf->len); | |
1250 | |
1251 buf_burn(buf); | |
1252 buf_free(buf); | |
1253 buf = NULL; | |
1254 | |
1255 header = "-----BEGIN OPENSSH PRIVATE KEY-----\n"; | |
1256 footer = "-----END OPENSSH PRIVATE KEY-----\n"; | |
1129 } | 1257 } |
1130 #endif | 1258 #endif |
1131 | 1259 |
1132 /* | 1260 /* |
1133 * Padding on OpenSSH keys is deterministic. The number of | 1261 * Padding on OpenSSH keys is deterministic. The number of |