Mercurial > dropbear
comparison keyimport.c @ 1908:eadd023fde4d
Support RSA OpenSSH new format in dropbearconvert
Added support for reading and writing. PEM writing support
has been removed.
OpenSSH file format routines have been moved to signkey_ossh.c
author | Matt Johnston <matt@ucc.asn.au> |
---|---|
date | Tue, 29 Mar 2022 22:27:55 +0800 |
parents | 3e0aacf0a4f3 |
children | ced53051e200 |
comparison
equal
deleted
inserted
replaced
1907:3e0aacf0a4f3 | 1908:eadd023fde4d |
---|---|
37 #include "ecc.h" | 37 #include "ecc.h" |
38 #include "ssh.h" | 38 #include "ssh.h" |
39 #include "rsa.h" | 39 #include "rsa.h" |
40 #include "dss.h" | 40 #include "dss.h" |
41 #include "ed25519.h" | 41 #include "ed25519.h" |
42 #include "signkey_ossh.h" | |
42 | 43 |
43 static const unsigned char OSSH_PKEY_BLOB[] = | 44 static const unsigned char OSSH_PKEY_BLOB[] = |
44 "openssh-key-v1\0" /* AUTH_MAGIC */ | 45 "openssh-key-v1\0" /* AUTH_MAGIC */ |
45 "\0\0\0\4none" /* cipher name*/ | 46 "\0\0\0\4none" /* cipher name*/ |
46 "\0\0\0\4none" /* kdf name */ | 47 "\0\0\0\4none" /* kdf name */ |
47 "\0\0\0\0" /* kdf */ | 48 "\0\0\0\0" /* kdf */ |
48 "\0\0\0\1"; /* key num */ | 49 "\0\0\0\1"; /* key num */ |
49 #define OSSH_PKEY_BLOBLEN (sizeof(OSSH_PKEY_BLOB) - 1) | 50 #define OSSH_PKEY_BLOBLEN (sizeof(OSSH_PKEY_BLOB) - 1) |
50 | |
51 void buf_put_ed25519_priv_ossh(buffer *buf, const sign_key *akey); | |
52 int buf_get_ed25519_priv_ossh(buffer *buf, sign_key *akey); | |
53 | |
54 #if DROPBEAR_ECDSA | 51 #if DROPBEAR_ECDSA |
55 static const unsigned char OID_SEC256R1_BLOB[] = {0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07}; | 52 static const unsigned char OID_SEC256R1_BLOB[] = {0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07}; |
56 static const unsigned char OID_SEC384R1_BLOB[] = {0x2b, 0x81, 0x04, 0x00, 0x22}; | 53 static const unsigned char OID_SEC384R1_BLOB[] = {0x2b, 0x81, 0x04, 0x00, 0x22}; |
57 static const unsigned char OID_SEC521R1_BLOB[] = {0x2b, 0x81, 0x04, 0x00, 0x23}; | 54 static const unsigned char OID_SEC521R1_BLOB[] = {0x2b, 0x81, 0x04, 0x00, 0x23}; |
58 #endif | 55 #endif |
569 | 566 |
570 if (!key) | 567 if (!key) |
571 return NULL; | 568 return NULL; |
572 | 569 |
573 if (key->encrypted) { | 570 if (key->encrypted) { |
574 errmsg = "encrypted keys not supported currently"; | 571 errmsg = "Encrypted keys are not supported. Please convert with ssh-keygen first"; |
575 goto error; | 572 goto error; |
576 #if 0 | |
577 /* matt TODO */ | |
578 /* | |
579 * Derive encryption key from passphrase and iv/salt: | |
580 * | |
581 * - let block A equal MD5(passphrase || iv) | |
582 * - let block B equal MD5(A || passphrase || iv) | |
583 * - block C would be MD5(B || passphrase || iv) and so on | |
584 * - encryption key is the first N bytes of A || B | |
585 */ | |
586 struct MD5Context md5c; | |
587 unsigned char keybuf[32]; | |
588 | |
589 MD5Init(&md5c); | |
590 MD5Update(&md5c, (unsigned char *)passphrase, strlen(passphrase)); | |
591 MD5Update(&md5c, (unsigned char *)key->iv, 8); | |
592 MD5Final(keybuf, &md5c); | |
593 | |
594 MD5Init(&md5c); | |
595 MD5Update(&md5c, keybuf, 16); | |
596 MD5Update(&md5c, (unsigned char *)passphrase, strlen(passphrase)); | |
597 MD5Update(&md5c, (unsigned char *)key->iv, 8); | |
598 MD5Final(keybuf+16, &md5c); | |
599 | |
600 /* | |
601 * Now decrypt the key blob. | |
602 */ | |
603 des3_decrypt_pubkey_ossh(keybuf, (unsigned char *)key->iv, | |
604 key->keyblob, key->keyblob_len); | |
605 | |
606 memset(&md5c, 0, sizeof(md5c)); | |
607 memset(keybuf, 0, sizeof(keybuf)); | |
608 #endif | |
609 } | 573 } |
610 | 574 |
611 /* | 575 /* |
612 * Now we have a decrypted key blob, which contains OpenSSH | 576 * Now we have a decrypted key blob, which contains OpenSSH |
613 * encoded private key. We must now untangle the OpenSSH format. | 577 * encoded private key. We must now untangle the OpenSSH format. |
638 /* discard checkkey2 */ | 602 /* discard checkkey2 */ |
639 buf_getint(blobbuf); | 603 buf_getint(blobbuf); |
640 | 604 |
641 if (type != DROPBEAR_SIGNKEY_NONE) { | 605 if (type != DROPBEAR_SIGNKEY_NONE) { |
642 retkey->type = type; | 606 retkey->type = type; |
607 #if DROPBEAR_RSA | |
608 if (type == DROPBEAR_SIGNKEY_RSA) { | |
609 if (buf_get_rsa_priv_ossh(blobbuf, retkey) | |
610 == DROPBEAR_SUCCESS) { | |
611 errmsg = NULL; | |
612 retval = retkey; | |
613 goto error; | |
614 } else { | |
615 errmsg = "Error parsing OpenSSH RSA key"; | |
616 goto ossh_error; | |
617 } | |
618 } | |
619 #endif | |
643 #if DROPBEAR_ED25519 | 620 #if DROPBEAR_ED25519 |
644 if (type == DROPBEAR_SIGNKEY_ED25519) { | 621 if (type == DROPBEAR_SIGNKEY_ED25519) { |
645 if (buf_get_ed25519_priv_ossh(blobbuf, retkey) | 622 if (buf_get_ed25519_priv_ossh(blobbuf, retkey) |
646 == DROPBEAR_SUCCESS) { | 623 == DROPBEAR_SUCCESS) { |
647 errmsg = NULL; | 624 errmsg = NULL; |
648 retval = retkey; | 625 retval = retkey; |
649 goto error; | 626 goto error; |
627 } else { | |
628 errmsg = "Error parsing OpenSSH ed25519 key"; | |
629 goto ossh_error; | |
650 } | 630 } |
651 } | 631 } |
652 #endif | 632 #endif |
653 } | 633 } |
654 | 634 |
915 fprintf(stderr, "Error: %s\n", errmsg); | 895 fprintf(stderr, "Error: %s\n", errmsg); |
916 } | 896 } |
917 return retval; | 897 return retval; |
918 } | 898 } |
919 | 899 |
920 #if DROPBEAR_ED25519 | |
921 /* OpenSSH raw private ed25519 format is | |
922 string "ssh-ed25519" | |
923 uint32 32 | |
924 byte[32] pubkey | |
925 uint32 64 | |
926 byte[32] privkey | |
927 byte[32] pubkey | |
928 */ | |
929 | |
930 void buf_put_ed25519_priv_ossh(buffer *buf, const sign_key *akey) { | |
931 const dropbear_ed25519_key *key = akey->ed25519key; | |
932 dropbear_assert(key != NULL); | |
933 buf_putstring(buf, SSH_SIGNKEY_ED25519, SSH_SIGNKEY_ED25519_LEN); | |
934 buf_putint(buf, CURVE25519_LEN); | |
935 buf_putbytes(buf, key->pub, CURVE25519_LEN); | |
936 buf_putint(buf, CURVE25519_LEN*2); | |
937 buf_putbytes(buf, key->priv, CURVE25519_LEN); | |
938 buf_putbytes(buf, key->pub, CURVE25519_LEN); | |
939 } | |
940 | |
941 int buf_get_ed25519_priv_ossh(buffer *buf, sign_key *akey) { | |
942 dropbear_ed25519_key *key = akey->ed25519key; | |
943 uint32_t len; | |
944 | |
945 dropbear_assert(key != NULL); | |
946 | |
947 /* Parse past the first string and pubkey */ | |
948 if (buf_get_ed25519_pub_key(buf, key, DROPBEAR_SIGNKEY_ED25519) | |
949 == DROPBEAR_FAILURE) { | |
950 dropbear_log(LOG_ERR, "Error parsing ed25519 key, pubkey"); | |
951 return DROPBEAR_FAILURE; | |
952 } | |
953 len = buf_getint(buf); | |
954 if (len != 2*CURVE25519_LEN) { | |
955 dropbear_log(LOG_ERR, "Error parsing ed25519 key, bad length"); | |
956 return DROPBEAR_FAILURE; | |
957 } | |
958 memcpy(key->priv, buf_getptr(buf, CURVE25519_LEN), CURVE25519_LEN); | |
959 buf_incrpos(buf, CURVE25519_LEN); | |
960 | |
961 /* Sanity check */ | |
962 if (memcmp(buf_getptr(buf, CURVE25519_LEN), key->pub, | |
963 CURVE25519_LEN) != 0) { | |
964 dropbear_log(LOG_ERR, "Error parsing ed25519 key, mismatch pubkey"); | |
965 return DROPBEAR_FAILURE; | |
966 } | |
967 return DROPBEAR_SUCCESS; | |
968 } | |
969 #endif | |
970 | |
971 static int openssh_write(const char *filename, sign_key *key, | 900 static int openssh_write(const char *filename, sign_key *key, |
972 const char *passphrase) | 901 const char *passphrase) |
973 { | 902 { |
974 buffer * keyblob = NULL; | 903 buffer * keyblob = NULL; |
975 buffer * extrablob = NULL; /* used for calculated values to write */ | 904 buffer * extrablob = NULL; /* used for calculated values to write */ |
980 char *header = NULL, *footer = NULL; | 909 char *header = NULL, *footer = NULL; |
981 char zero[1]; | 910 char zero[1]; |
982 int ret = 0; | 911 int ret = 0; |
983 FILE *fp; | 912 FILE *fp; |
984 | 913 |
985 #if DROPBEAR_RSA | |
986 mp_int dmp1, dmq1, iqmp, tmpval; /* for rsa */ | |
987 #endif | |
988 | |
989 if ( | 914 if ( |
990 #if DROPBEAR_RSA | |
991 key->type == DROPBEAR_SIGNKEY_RSA || | |
992 #endif | |
993 #if DROPBEAR_DSS | 915 #if DROPBEAR_DSS |
994 key->type == DROPBEAR_SIGNKEY_DSS || | 916 key->type == DROPBEAR_SIGNKEY_DSS || |
995 #endif | 917 #endif |
996 0) | 918 0) |
997 { | 919 { |
1009 * Find the sequence of integers to be encoded into the OpenSSH | 931 * Find the sequence of integers to be encoded into the OpenSSH |
1010 * key blob, and also decide on the header line. | 932 * key blob, and also decide on the header line. |
1011 */ | 933 */ |
1012 numbers[0].start = zero; numbers[0].bytes = 1; zero[0] = '\0'; | 934 numbers[0].start = zero; numbers[0].bytes = 1; zero[0] = '\0'; |
1013 | 935 |
1014 #if DROPBEAR_RSA | |
1015 if (key->type == DROPBEAR_SIGNKEY_RSA) { | |
1016 | |
1017 if (key->rsakey->p == NULL || key->rsakey->q == NULL) { | |
1018 fprintf(stderr, "Pre-0.33 Dropbear keys cannot be converted to OpenSSH keys.\n"); | |
1019 goto error; | |
1020 } | |
1021 | |
1022 /* e */ | |
1023 numbers[2].bytes = buf_getint(keyblob); | |
1024 numbers[2].start = buf_getptr(keyblob, numbers[2].bytes); | |
1025 buf_incrpos(keyblob, numbers[2].bytes); | |
1026 | |
1027 /* n */ | |
1028 numbers[1].bytes = buf_getint(keyblob); | |
1029 numbers[1].start = buf_getptr(keyblob, numbers[1].bytes); | |
1030 buf_incrpos(keyblob, numbers[1].bytes); | |
1031 | |
1032 /* d */ | |
1033 numbers[3].bytes = buf_getint(keyblob); | |
1034 numbers[3].start = buf_getptr(keyblob, numbers[3].bytes); | |
1035 buf_incrpos(keyblob, numbers[3].bytes); | |
1036 | |
1037 /* p */ | |
1038 numbers[4].bytes = buf_getint(keyblob); | |
1039 numbers[4].start = buf_getptr(keyblob, numbers[4].bytes); | |
1040 buf_incrpos(keyblob, numbers[4].bytes); | |
1041 | |
1042 /* q */ | |
1043 numbers[5].bytes = buf_getint(keyblob); | |
1044 numbers[5].start = buf_getptr(keyblob, numbers[5].bytes); | |
1045 buf_incrpos(keyblob, numbers[5].bytes); | |
1046 | |
1047 /* now calculate some extra parameters: */ | |
1048 m_mp_init(&tmpval); | |
1049 m_mp_init(&dmp1); | |
1050 m_mp_init(&dmq1); | |
1051 m_mp_init(&iqmp); | |
1052 | |
1053 /* dmp1 = d mod (p-1) */ | |
1054 if (mp_sub_d(key->rsakey->p, 1, &tmpval) != MP_OKAY) { | |
1055 fprintf(stderr, "Bignum error for p-1\n"); | |
1056 goto error; | |
1057 } | |
1058 if (mp_mod(key->rsakey->d, &tmpval, &dmp1) != MP_OKAY) { | |
1059 fprintf(stderr, "Bignum error for dmp1\n"); | |
1060 goto error; | |
1061 } | |
1062 | |
1063 /* dmq1 = d mod (q-1) */ | |
1064 if (mp_sub_d(key->rsakey->q, 1, &tmpval) != MP_OKAY) { | |
1065 fprintf(stderr, "Bignum error for q-1\n"); | |
1066 goto error; | |
1067 } | |
1068 if (mp_mod(key->rsakey->d, &tmpval, &dmq1) != MP_OKAY) { | |
1069 fprintf(stderr, "Bignum error for dmq1\n"); | |
1070 goto error; | |
1071 } | |
1072 | |
1073 /* iqmp = (q^-1) mod p */ | |
1074 if (mp_invmod(key->rsakey->q, key->rsakey->p, &iqmp) != MP_OKAY) { | |
1075 fprintf(stderr, "Bignum error for iqmp\n"); | |
1076 goto error; | |
1077 } | |
1078 | |
1079 extrablob = buf_new(2000); | |
1080 buf_putmpint(extrablob, &dmp1); | |
1081 buf_putmpint(extrablob, &dmq1); | |
1082 buf_putmpint(extrablob, &iqmp); | |
1083 buf_setpos(extrablob, 0); | |
1084 mp_clear(&dmp1); | |
1085 mp_clear(&dmq1); | |
1086 mp_clear(&iqmp); | |
1087 mp_clear(&tmpval); | |
1088 | |
1089 /* dmp1 */ | |
1090 numbers[6].bytes = buf_getint(extrablob); | |
1091 numbers[6].start = buf_getptr(extrablob, numbers[6].bytes); | |
1092 buf_incrpos(extrablob, numbers[6].bytes); | |
1093 | |
1094 /* dmq1 */ | |
1095 numbers[7].bytes = buf_getint(extrablob); | |
1096 numbers[7].start = buf_getptr(extrablob, numbers[7].bytes); | |
1097 buf_incrpos(extrablob, numbers[7].bytes); | |
1098 | |
1099 /* iqmp */ | |
1100 numbers[8].bytes = buf_getint(extrablob); | |
1101 numbers[8].start = buf_getptr(extrablob, numbers[8].bytes); | |
1102 buf_incrpos(extrablob, numbers[8].bytes); | |
1103 | |
1104 nnumbers = 9; | |
1105 header = "-----BEGIN RSA PRIVATE KEY-----\n"; | |
1106 footer = "-----END RSA PRIVATE KEY-----\n"; | |
1107 } | |
1108 #endif /* DROPBEAR_RSA */ | |
1109 | |
1110 #if DROPBEAR_DSS | 936 #if DROPBEAR_DSS |
1111 if (key->type == DROPBEAR_SIGNKEY_DSS) { | 937 if (key->type == DROPBEAR_SIGNKEY_DSS) { |
1112 | 938 |
1113 /* p */ | 939 /* p */ |
1114 numbers[1].bytes = buf_getint(keyblob); | 940 numbers[1].bytes = buf_getint(keyblob); |
1172 for (i = 0; i < nnumbers; i++) { | 998 for (i = 0; i < nnumbers; i++) { |
1173 pos += ber_write_id_len(outblob+pos, 2, numbers[i].bytes, 0); | 999 pos += ber_write_id_len(outblob+pos, 2, numbers[i].bytes, 0); |
1174 memcpy(outblob+pos, numbers[i].start, numbers[i].bytes); | 1000 memcpy(outblob+pos, numbers[i].start, numbers[i].bytes); |
1175 pos += numbers[i].bytes; | 1001 pos += numbers[i].bytes; |
1176 } | 1002 } |
1177 } /* end RSA and DSS handling */ | 1003 } /* end DSS handling */ |
1178 | 1004 |
1179 #if DROPBEAR_ECDSA | 1005 #if DROPBEAR_ECDSA |
1180 if (key->type == DROPBEAR_SIGNKEY_ECDSA_NISTP256 | 1006 if (key->type == DROPBEAR_SIGNKEY_ECDSA_NISTP256 |
1181 || key->type == DROPBEAR_SIGNKEY_ECDSA_NISTP384 | 1007 || key->type == DROPBEAR_SIGNKEY_ECDSA_NISTP384 |
1182 || key->type == DROPBEAR_SIGNKEY_ECDSA_NISTP521) { | 1008 || key->type == DROPBEAR_SIGNKEY_ECDSA_NISTP521) { |
1271 header = "-----BEGIN EC PRIVATE KEY-----\n"; | 1097 header = "-----BEGIN EC PRIVATE KEY-----\n"; |
1272 footer = "-----END EC PRIVATE KEY-----\n"; | 1098 footer = "-----END EC PRIVATE KEY-----\n"; |
1273 } | 1099 } |
1274 #endif | 1100 #endif |
1275 | 1101 |
1102 if (0 | |
1103 #if DROPBEAR_RSA | |
1104 || key->type == DROPBEAR_SIGNKEY_RSA | |
1105 #endif | |
1276 #if DROPBEAR_ED25519 | 1106 #if DROPBEAR_ED25519 |
1277 if (key->type == DROPBEAR_SIGNKEY_ED25519) { | 1107 || key->type == DROPBEAR_SIGNKEY_ED25519 |
1278 buffer *buf = buf_new(1200); | 1108 #endif |
1279 keyblob = buf_new(1000); | 1109 ) { |
1280 extrablob = buf_new(1100); | 1110 buffer *buf = buf_new(3200); |
1111 keyblob = buf_new(3000); | |
1112 extrablob = buf_new(3100); | |
1281 | 1113 |
1282 /* private key blob w/o header */ | 1114 /* private key blob w/o header */ |
1283 buf_put_ed25519_priv_ossh(keyblob, key); | 1115 #if DROPBEAR_RSA |
1116 if (key->type == DROPBEAR_SIGNKEY_RSA) { | |
1117 buf_put_rsa_priv_ossh(keyblob, key); | |
1118 } | |
1119 #endif | |
1120 #if DROPBEAR_ED25519 | |
1121 if (key->type == DROPBEAR_SIGNKEY_ED25519) { | |
1122 buf_put_ed25519_priv_ossh(keyblob, key); | |
1123 } | |
1124 #endif | |
1284 | 1125 |
1285 /* header */ | 1126 /* header */ |
1286 buf_putbytes(buf, OSSH_PKEY_BLOB, OSSH_PKEY_BLOBLEN); | 1127 buf_putbytes(buf, OSSH_PKEY_BLOB, OSSH_PKEY_BLOBLEN); |
1287 | 1128 |
1288 /* public key */ | 1129 /* public key */ |
1312 buf = NULL; | 1153 buf = NULL; |
1313 | 1154 |
1314 header = "-----BEGIN OPENSSH PRIVATE KEY-----\n"; | 1155 header = "-----BEGIN OPENSSH PRIVATE KEY-----\n"; |
1315 footer = "-----END OPENSSH PRIVATE KEY-----\n"; | 1156 footer = "-----END OPENSSH PRIVATE KEY-----\n"; |
1316 } | 1157 } |
1317 #endif | |
1318 | 1158 |
1319 /* | 1159 /* |
1320 * Padding on OpenSSH keys is deterministic. The number of | 1160 * Padding on OpenSSH keys is deterministic. The number of |
1321 * padding bytes is always more than zero, and always at most | 1161 * padding bytes is always more than zero, and always at most |
1322 * the cipher block length. The value of each padding byte is | 1162 * the cipher block length. The value of each padding byte is |