|
@@ -12,8 +12,10 @@
|
|
OpenSSH Private Key decryption demo, Steffen Jaeckel
|
|
OpenSSH Private Key decryption demo, Steffen Jaeckel
|
|
*/
|
|
*/
|
|
|
|
|
|
|
|
+#define _GNU_SOURCE
|
|
|
|
+
|
|
#include <bsd/string.h>
|
|
#include <bsd/string.h>
|
|
-#include <tomcrypt.h>
|
|
|
|
|
|
+#include <tomcrypt_private.h>
|
|
#include <stdarg.h>
|
|
#include <stdarg.h>
|
|
|
|
|
|
static int verbose = 1;
|
|
static int verbose = 1;
|
|
@@ -71,15 +73,34 @@ static void die_(int err, int line)
|
|
}
|
|
}
|
|
|
|
|
|
#define die(i) do { die_(i, __LINE__); } while(0)
|
|
#define die(i) do { die_(i, __LINE__); } while(0)
|
|
-#define DIE(s) do { print_err("%3d: " s "\n", __LINE__); exit(EXIT_FAILURE); } while(0)
|
|
|
|
|
|
+#define DIE(s, ...) do { print_err("%3d: " s "\n", __LINE__, ##__VA_ARGS__); exit(EXIT_FAILURE); } while(0)
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+static void check_padding(unsigned char *p, unsigned long len)
|
|
|
|
+{
|
|
|
|
+ unsigned char pad = 0x1u;
|
|
|
|
+ while (len != 0) {
|
|
|
|
+ if (*p != pad) DIE("pad wrong 0x%02x != 0x%02x", *p, pad);
|
|
|
|
+ p++;
|
|
|
|
+ pad++;
|
|
|
|
+ len--;
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
|
|
|
|
+typedef struct pka_key_ {
|
|
|
|
+ enum ltc_oid_id id;
|
|
|
|
+ union {
|
|
|
|
+ curve25519_key ed25519;
|
|
|
|
+ rsa_key rsa;
|
|
|
|
+ } u;
|
|
|
|
+} pka_key;
|
|
|
|
|
|
enum blockcipher_mode {
|
|
enum blockcipher_mode {
|
|
none, cbc, ctr, stream, gcm
|
|
none, cbc, ctr, stream, gcm
|
|
};
|
|
};
|
|
struct ssh_blockcipher {
|
|
struct ssh_blockcipher {
|
|
const char *name;
|
|
const char *name;
|
|
- const char *cipher;
|
|
|
|
|
|
+ const char *algo;
|
|
int len;
|
|
int len;
|
|
enum blockcipher_mode mode;
|
|
enum blockcipher_mode mode;
|
|
};
|
|
};
|
|
@@ -89,29 +110,153 @@ struct ssh_blockcipher {
|
|
*/
|
|
*/
|
|
const struct ssh_blockcipher ssh_ciphers[] =
|
|
const struct ssh_blockcipher ssh_ciphers[] =
|
|
{
|
|
{
|
|
|
|
+ { "none", "", 0, none },
|
|
{ "aes256-cbc", "aes", 256 / 8, cbc },
|
|
{ "aes256-cbc", "aes", 256 / 8, cbc },
|
|
{ 0 },
|
|
{ 0 },
|
|
};
|
|
};
|
|
|
|
|
|
|
|
+struct ssh_kdf {
|
|
|
|
+ const char *name;
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+struct kdf_options {
|
|
|
|
+ struct ssh_blockcipher *cipher;
|
|
|
|
+ unsigned char salt[64];
|
|
|
|
+ ulong32 saltlen;
|
|
|
|
+ ulong32 num_rounds;
|
|
|
|
+ const char *pass;
|
|
|
|
+ unsigned long passlen;
|
|
|
|
+};
|
|
|
|
|
|
-struct ssh_decode_ctx {
|
|
|
|
- unsigned char *orig;
|
|
|
|
- size_t len, rd;
|
|
|
|
- void *cur;
|
|
|
|
|
|
+const struct ssh_kdf ssh_kdfs[] =
|
|
|
|
+{
|
|
|
|
+ { "bcrypt" },
|
|
|
|
+ { "none" },
|
|
|
|
+ { 0 },
|
|
};
|
|
};
|
|
|
|
|
|
-ulong32 read_length_and_advance(int is_length, struct ssh_decode_ctx *ctx) {
|
|
|
|
- ulong32 r;
|
|
|
|
- ctx->cur = &ctx->orig[ctx->rd];
|
|
|
|
|
|
+int ssh_decode_private_key(const unsigned char *in, unsigned long *inlen, pka_key *key)
|
|
|
|
+{
|
|
|
|
+ int err;
|
|
|
|
+ ulong32 check1, check2;
|
|
|
|
+ unsigned char pka[64], pubkey[2048], privkey[2048], comment[256];
|
|
|
|
+ ulong32 pkalen = sizeof(pka), pubkeylen = sizeof(pubkey);
|
|
|
|
+ ulong32 privkeylen = sizeof(privkey), commentlen = sizeof(comment);
|
|
|
|
+ unsigned long remaining, cur_len;
|
|
|
|
+ unsigned char *p;
|
|
|
|
+
|
|
|
|
+ LTC_ARGCHK(in != NULL);
|
|
|
|
+ LTC_ARGCHK(inlen != NULL);
|
|
|
|
+ LTC_ARGCHK(key != NULL);
|
|
|
|
+
|
|
|
|
+ p = in;
|
|
|
|
+ cur_len = *inlen;
|
|
|
|
+
|
|
|
|
+ if ((err = ssh_decode_sequence_multi(p, &cur_len,
|
|
|
|
+ LTC_SSHDATA_UINT32, &check1,
|
|
|
|
+ LTC_SSHDATA_UINT32, &check2,
|
|
|
|
+ LTC_SSHDATA_STRING, pka, &pkalen,
|
|
|
|
+ LTC_SSHDATA_EOL)) != CRYPT_OK) {
|
|
|
|
+ die(err);
|
|
|
|
+ }
|
|
|
|
+ if (check1 != check2) DIE("decrypt failed");
|
|
|
|
+
|
|
|
|
+ p += cur_len;
|
|
|
|
+ remaining = *inlen - cur_len;
|
|
|
|
+
|
|
|
|
+ if (XSTRCMP(pka, "ssh-ed25519") == 0) {
|
|
|
|
+ cur_len = remaining;
|
|
|
|
+ if ((err = ssh_decode_sequence_multi(p, &cur_len,
|
|
|
|
+ LTC_SSHDATA_STRING, pubkey, &pubkeylen,
|
|
|
|
+ LTC_SSHDATA_STRING, privkey, &privkeylen,
|
|
|
|
+ LTC_SSHDATA_EOL)) != CRYPT_OK) {
|
|
|
|
+ die(err);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if ((err = ed25519_set_key(privkey, 32, &privkey[32], 32, &key->u.ed25519)) != CRYPT_OK) {
|
|
|
|
+ die(err);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ key->id = PKA_ED25519;
|
|
|
|
+ } else if (XSTRCMP(pka, "ssh-rsa") == 0) {
|
|
|
|
+ void *tmp1, *tmp2;
|
|
|
|
+ if ((err = mp_init_multi(&tmp1, &tmp2, NULL)) != CRYPT_OK) {
|
|
|
|
+ die(err);
|
|
|
|
+ }
|
|
|
|
+ if ((err = rsa_init(&key->u.rsa)) != CRYPT_OK) {
|
|
|
|
+ die(err);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ cur_len = remaining;
|
|
|
|
+ if ((err = ssh_decode_sequence_multi(p, &cur_len,
|
|
|
|
+ LTC_SSHDATA_MPINT, key->u.rsa.N,
|
|
|
|
+ LTC_SSHDATA_MPINT, key->u.rsa.e,
|
|
|
|
+ LTC_SSHDATA_MPINT, key->u.rsa.d,
|
|
|
|
+ LTC_SSHDATA_MPINT, key->u.rsa.qP,
|
|
|
|
+ LTC_SSHDATA_MPINT, key->u.rsa.q,
|
|
|
|
+ LTC_SSHDATA_MPINT, key->u.rsa.p,
|
|
|
|
+ LTC_SSHDATA_EOL)) != CRYPT_OK) {
|
|
|
|
+ die(err);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if ((err = mp_sub_d(key->u.rsa.p, 1, tmp1)) != CRYPT_OK) { die(err); } /* tmp1 = q-1 */
|
|
|
|
+ if ((err = mp_sub_d(key->u.rsa.q, 1, tmp2)) != CRYPT_OK) { die(err); } /* tmp2 = p-1 */
|
|
|
|
+ if ((err = mp_mod( key->u.rsa.d, tmp1, key->u.rsa.dP)) != CRYPT_OK) { die(err); } /* dP = d mod p-1 */
|
|
|
|
+ if ((err = mp_mod( key->u.rsa.d, tmp2, key->u.rsa.dQ)) != CRYPT_OK) { die(err); } /* dQ = d mod q-1 */
|
|
|
|
+
|
|
|
|
+ mp_clear_multi(tmp2, tmp1, NULL);
|
|
|
|
+ } else {
|
|
|
|
+ DIE("unsupported pka");
|
|
|
|
+ }
|
|
|
|
+ p += cur_len;
|
|
|
|
+ remaining -= cur_len;
|
|
|
|
+
|
|
|
|
+ cur_len = remaining;
|
|
|
|
+ if ((err = ssh_decode_sequence_multi(p, &cur_len,
|
|
|
|
+ LTC_SSHDATA_STRING, comment, &commentlen,
|
|
|
|
+ LTC_SSHDATA_EOL)) != CRYPT_OK) {
|
|
|
|
+ die(err);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ printf("comment: %s\n", comment);
|
|
|
|
+
|
|
|
|
+ p += cur_len;
|
|
|
|
+ remaining -= cur_len;
|
|
|
|
|
|
- LOAD32H(r, ctx->cur);
|
|
|
|
- print_err("%u\n", r);
|
|
|
|
- if (is_length && ctx->rd + r > ctx->len) DIE("too long");
|
|
|
|
- ctx->rd += 4;
|
|
|
|
|
|
+ check_padding(p, remaining);
|
|
|
|
|
|
- ctx->cur = &ctx->orig[ctx->rd];
|
|
|
|
- if (is_length) print_hex("next", ctx->cur, r);
|
|
|
|
- return r;
|
|
|
|
|
|
+ return err;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+int ssh_decrypt_private_keys(unsigned char *in, unsigned long *inlen, struct kdf_options *opts)
|
|
|
|
+{
|
|
|
|
+ int err, cipher;
|
|
|
|
+ unsigned char symkey[128];
|
|
|
|
+ unsigned long cur_len, symkey_len;
|
|
|
|
+
|
|
|
|
+ LTC_ARGCHK(in != NULL);
|
|
|
|
+ LTC_ARGCHK(inlen != NULL);
|
|
|
|
+ LTC_ARGCHK(opts != NULL);
|
|
|
|
+
|
|
|
|
+ cipher = find_cipher(opts->cipher->algo);
|
|
|
|
+ symkey_len = opts->cipher->len + cipher_descriptor[cipher].block_length;
|
|
|
|
+
|
|
|
|
+ if (sizeof(symkey) < symkey_len) DIE("too small");
|
|
|
|
+
|
|
|
|
+ if ((err = bcrypt_pbkdf_openbsd(opts->pass, opts->passlen, opts->salt, opts->saltlen, opts->num_rounds, find_hash("sha512"), symkey, &symkey_len)) != CRYPT_OK) {
|
|
|
|
+ die(err);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ symmetric_CBC cbc_ctx;
|
|
|
|
+ if ((err = cbc_start(cipher, symkey + opts->cipher->len, symkey, opts->cipher->len, 0, &cbc_ctx)) != CRYPT_OK) {
|
|
|
|
+ die(err);
|
|
|
|
+ }
|
|
|
|
+ if ((err = cbc_decrypt(in, in, *inlen, &cbc_ctx)) != CRYPT_OK) {
|
|
|
|
+ die(err);
|
|
|
|
+ }
|
|
|
|
+ print_hex("decrypted", in, *inlen);
|
|
|
|
+
|
|
|
|
+ return err;
|
|
}
|
|
}
|
|
|
|
|
|
/* The basic format of the key is described here:
|
|
/* The basic format of the key is described here:
|
|
@@ -120,8 +265,6 @@ ulong32 read_length_and_advance(int is_length, struct ssh_decode_ctx *ctx) {
|
|
|
|
|
|
int main(int argc, char **argv)
|
|
int main(int argc, char **argv)
|
|
{
|
|
{
|
|
- if (argc < 2) return EXIT_FAILURE;
|
|
|
|
-
|
|
|
|
int err;
|
|
int err;
|
|
if ((err = register_all_ciphers()) != CRYPT_OK) {
|
|
if ((err = register_all_ciphers()) != CRYPT_OK) {
|
|
die(err);
|
|
die(err);
|
|
@@ -129,30 +272,41 @@ int main(int argc, char **argv)
|
|
if ((err = register_all_hashes()) != CRYPT_OK) {
|
|
if ((err = register_all_hashes()) != CRYPT_OK) {
|
|
die(err);
|
|
die(err);
|
|
}
|
|
}
|
|
|
|
+ if ((err = crypt_mp_init("ltm")) != CRYPT_OK) {
|
|
|
|
+ die(err);
|
|
|
|
+ }
|
|
|
|
|
|
char pem[100 * 72];
|
|
char pem[100 * 72];
|
|
size_t w = 0;
|
|
size_t w = 0;
|
|
- const char *b64_start = "-----BEGIN OPENSSH PRIVATE KEY-----";
|
|
|
|
- const char *b64_end = "-----END OPENSSH PRIVATE KEY-----";
|
|
|
|
|
|
+ const char *openssh_privkey_start = "-----BEGIN OPENSSH PRIVATE KEY-----";
|
|
|
|
+ const char *openssh_privkey_end = "-----END OPENSSH PRIVATE KEY-----";
|
|
char buf[72];
|
|
char buf[72];
|
|
- FILE *f = fopen(argv[1], "r");
|
|
|
|
|
|
+ FILE *f = NULL;
|
|
|
|
+
|
|
|
|
+ if (argc > 1) f = fopen(argv[1], "r");
|
|
|
|
+ else f = stdin;
|
|
if (f == NULL) DIE("fopen sez no");
|
|
if (f == NULL) DIE("fopen sez no");
|
|
|
|
+
|
|
while (fgets(buf, sizeof(buf), f)) {
|
|
while (fgets(buf, sizeof(buf), f)) {
|
|
- const char *start = strstr(buf, b64_start);
|
|
|
|
- if (start == buf) break;
|
|
|
|
|
|
+ const char *start = strstr(buf, openssh_privkey_start);
|
|
|
|
+ if (start != NULL) {
|
|
|
|
+ start += strlen(openssh_privkey_start);
|
|
|
|
+ size_t l = strlcpy(pem + w, start, sizeof(pem) - w);
|
|
|
|
+ w += l;
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
}
|
|
}
|
|
while (fgets(buf, sizeof(buf), f)) {
|
|
while (fgets(buf, sizeof(buf), f)) {
|
|
- char *end = strstr(buf, b64_end);
|
|
|
|
- if (end != NULL) {
|
|
|
|
- if (end == buf) break;
|
|
|
|
- *end = '\0';
|
|
|
|
- }
|
|
|
|
size_t l = strlcpy(pem + w, buf, sizeof(pem) - w);
|
|
size_t l = strlcpy(pem + w, buf, sizeof(pem) - w);
|
|
if (l == 0) {
|
|
if (l == 0) {
|
|
DIE("strlcpy sez no");
|
|
DIE("strlcpy sez no");
|
|
}
|
|
}
|
|
w += l;
|
|
w += l;
|
|
}
|
|
}
|
|
|
|
+ char *end = strstr(pem, openssh_privkey_end);
|
|
|
|
+ if (end == NULL) DIE("could not find PEM end-tag");
|
|
|
|
+ *end = '\0';
|
|
|
|
+ w = end - pem;
|
|
|
|
|
|
unsigned char b64_decoded[sizeof(pem)];
|
|
unsigned char b64_decoded[sizeof(pem)];
|
|
unsigned long b64_decoded_len = sizeof(pem);
|
|
unsigned long b64_decoded_len = sizeof(pem);
|
|
@@ -164,103 +318,90 @@ int main(int argc, char **argv)
|
|
void *magic = strstr((const char*)b64_decoded, "openssh-key-v1");
|
|
void *magic = strstr((const char*)b64_decoded, "openssh-key-v1");
|
|
if (magic == NULL) DIE("magic not found");
|
|
if (magic == NULL) DIE("magic not found");
|
|
if (magic != b64_decoded) DIE("magic not at the beginning");
|
|
if (magic != b64_decoded) DIE("magic not at the beginning");
|
|
- struct ssh_decode_ctx dec_ctx;
|
|
|
|
- dec_ctx.orig = b64_decoded;
|
|
|
|
- dec_ctx.len = b64_decoded_len;
|
|
|
|
- dec_ctx.rd = strlen("openssh-key-v1") + 1;
|
|
|
|
|
|
|
|
- ulong32 l = read_length_and_advance(1, &dec_ctx);
|
|
|
|
|
|
+ size_t nlen = strlen("openssh-key-v1");
|
|
|
|
+ unsigned char *start = &b64_decoded[nlen + 1];
|
|
|
|
+ unsigned long tot_len = b64_decoded_len - nlen - 1;
|
|
|
|
+
|
|
|
|
+ unsigned char ciphername[64];
|
|
|
|
+ ulong32 ciphernamelen = sizeof(ciphername);
|
|
|
|
+ unsigned char kdfname[64];
|
|
|
|
+ ulong32 kdfnamelen = sizeof(kdfname);
|
|
|
|
+ unsigned char kdfoptions[128];
|
|
|
|
+ ulong32 kdfoptionslen = sizeof(kdfoptions);
|
|
|
|
+ ulong32 num_keys;
|
|
|
|
+ unsigned char pubkey1[2048];
|
|
|
|
+ ulong32 pubkey1len = sizeof(pubkey1);
|
|
|
|
+
|
|
|
|
+ unsigned long cur_len = tot_len;
|
|
|
|
+
|
|
|
|
+ if ((err = ssh_decode_sequence_multi(start, &cur_len,
|
|
|
|
+ LTC_SSHDATA_STRING, ciphername, &ciphernamelen,
|
|
|
|
+ LTC_SSHDATA_STRING, kdfname, &kdfnamelen,
|
|
|
|
+ LTC_SSHDATA_STRING, kdfoptions, &kdfoptionslen,
|
|
|
|
+ LTC_SSHDATA_UINT32, &num_keys,
|
|
|
|
+ LTC_SSHDATA_STRING, pubkey1, &pubkey1len,
|
|
|
|
+ LTC_SSHDATA_EOL)) != CRYPT_OK) {
|
|
|
|
+ die(err);
|
|
|
|
+ }
|
|
|
|
+ if (num_keys != 1) DIE("more than 1 pubkey not supported");
|
|
|
|
+
|
|
|
|
+ print_hex("public key", pubkey1, pubkey1len);
|
|
|
|
+
|
|
|
|
+ start += cur_len;
|
|
|
|
+ tot_len -= cur_len;
|
|
|
|
+ cur_len = tot_len;
|
|
|
|
|
|
const struct ssh_blockcipher *c = NULL;
|
|
const struct ssh_blockcipher *c = NULL;
|
|
for (size_t i = 0; i < sizeof(ssh_ciphers)/sizeof(ssh_ciphers[0]); ++i) {
|
|
for (size_t i = 0; i < sizeof(ssh_ciphers)/sizeof(ssh_ciphers[0]); ++i) {
|
|
- if (memcmp(dec_ctx.cur, ssh_ciphers[i].name, l) == 0) {
|
|
|
|
|
|
+ nlen = strlen(ssh_ciphers[i].name);
|
|
|
|
+ if ((nlen == ciphernamelen) && (XMEMCMP(ciphername, ssh_ciphers[i].name, nlen) == 0)) {
|
|
c = &ssh_ciphers[i];
|
|
c = &ssh_ciphers[i];
|
|
break;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
- if (c == NULL) DIE("can't find cipher");
|
|
|
|
- dec_ctx.rd += l;
|
|
|
|
-
|
|
|
|
- l = read_length_and_advance(1, &dec_ctx);
|
|
|
|
-
|
|
|
|
- if (memcmp(dec_ctx.cur, "bcrypt", l) != 0) DIE("unsupported kdf");
|
|
|
|
- dec_ctx.rd += l;
|
|
|
|
-
|
|
|
|
- l = read_length_and_advance(1, &dec_ctx);
|
|
|
|
-
|
|
|
|
- unsigned char salt[32];
|
|
|
|
- unsigned long salt_len = read_length_and_advance(1, &dec_ctx);
|
|
|
|
-
|
|
|
|
- memcpy(salt, dec_ctx.cur, salt_len);
|
|
|
|
- dec_ctx.rd += salt_len;
|
|
|
|
-
|
|
|
|
- ulong32 rounds = read_length_and_advance(0, &dec_ctx);
|
|
|
|
-
|
|
|
|
- ulong32 num_pubkeys = read_length_and_advance(1, &dec_ctx);
|
|
|
|
|
|
+ if (c == NULL) DIE("can't find algo");
|
|
|
|
|
|
- l = read_length_and_advance(1, &dec_ctx);
|
|
|
|
- l = read_length_and_advance(1, &dec_ctx);
|
|
|
|
- if (memcmp(dec_ctx.cur, "ssh-ed25519", l) != 0) DIE("unsupported pka");
|
|
|
|
- dec_ctx.rd += l;
|
|
|
|
|
|
+ struct kdf_options opts;
|
|
|
|
+ opts.saltlen = sizeof(opts.salt);
|
|
|
|
+ opts.cipher = c;
|
|
|
|
+ opts.pass = "abc123";
|
|
|
|
+ opts.passlen = 6;
|
|
|
|
|
|
- unsigned char pubkey[32];
|
|
|
|
- unsigned long pubkey_len = read_length_and_advance(1, &dec_ctx);
|
|
|
|
|
|
+ unsigned char privkey[sizeof(pem)];
|
|
|
|
+ ulong32 privkey_len = sizeof(privkey);
|
|
|
|
|
|
- memcpy(pubkey, dec_ctx.cur, pubkey_len);
|
|
|
|
- dec_ctx.rd += pubkey_len;
|
|
|
|
-
|
|
|
|
- l = read_length_and_advance(1, &dec_ctx);
|
|
|
|
-
|
|
|
|
- unsigned char decrypted_privkey[sizeof(pem)];
|
|
|
|
- unsigned long decrypted_privkey_len = sizeof(decrypted_privkey);
|
|
|
|
-
|
|
|
|
- int cipher = find_cipher(c->cipher);
|
|
|
|
-
|
|
|
|
- unsigned char symkey[128];
|
|
|
|
- unsigned long symkey_len = c->len + cipher_descriptor[cipher].block_length;
|
|
|
|
-
|
|
|
|
- if (sizeof(symkey) < symkey_len) DIE("too small");
|
|
|
|
-
|
|
|
|
- if ((err = bcrypt("abc123", 6, salt, salt_len, rounds, find_hash("sha512"), symkey, &symkey_len)) != CRYPT_OK) {
|
|
|
|
|
|
+ cur_len = tot_len;
|
|
|
|
+ if ((err = ssh_decode_sequence_multi(start, &cur_len,
|
|
|
|
+ LTC_SSHDATA_STRING, privkey, &privkey_len,
|
|
|
|
+ LTC_SSHDATA_EOL)) != CRYPT_OK) {
|
|
die(err);
|
|
die(err);
|
|
}
|
|
}
|
|
|
|
|
|
- symmetric_CBC cbc_ctx;
|
|
|
|
- if ((err = cbc_start(cipher, symkey + c->len, symkey, c->len, 0, &cbc_ctx)) != CRYPT_OK) {
|
|
|
|
- die(err);
|
|
|
|
- }
|
|
|
|
- if ((err = cbc_decrypt(dec_ctx.cur, decrypted_privkey, l, &cbc_ctx)) != CRYPT_OK) {
|
|
|
|
- die(err);
|
|
|
|
- }
|
|
|
|
- decrypted_privkey_len = l;
|
|
|
|
- print_hex("decrypted", decrypted_privkey, decrypted_privkey_len);
|
|
|
|
-
|
|
|
|
- dec_ctx.orig = decrypted_privkey;
|
|
|
|
- dec_ctx.len = decrypted_privkey_len;
|
|
|
|
- dec_ctx.rd = 0;
|
|
|
|
-
|
|
|
|
- ulong32 check1 = read_length_and_advance(0, &dec_ctx);
|
|
|
|
- ulong32 check2 = read_length_and_advance(0, &dec_ctx);
|
|
|
|
-
|
|
|
|
- if (check1 != check2) DIE("decrypt failed");
|
|
|
|
-
|
|
|
|
- l = read_length_and_advance(1, &dec_ctx);
|
|
|
|
- if (memcmp(dec_ctx.cur, "ssh-ed25519", l) != 0) DIE("unsupported pka");
|
|
|
|
- dec_ctx.rd += l;
|
|
|
|
-
|
|
|
|
- l = read_length_and_advance(1, &dec_ctx);
|
|
|
|
- if (memcmp(dec_ctx.cur, pubkey, l) != 0) DIE("pubkey's don't match");
|
|
|
|
- dec_ctx.rd += l;
|
|
|
|
-
|
|
|
|
- unsigned char privkey[64];
|
|
|
|
- unsigned long privkey_len = read_length_and_advance(1, &dec_ctx);
|
|
|
|
|
|
+ if (XSTRCMP(kdfname, "none") == 0) {
|
|
|
|
+ /* NOP */
|
|
|
|
+ } else if (XSTRCMP(kdfname, "bcrypt") == 0) {
|
|
|
|
+ cur_len = kdfoptionslen;
|
|
|
|
+ if ((err = ssh_decode_sequence_multi(kdfoptions, &cur_len,
|
|
|
|
+ LTC_SSHDATA_STRING, opts.salt, &opts.saltlen,
|
|
|
|
+ LTC_SSHDATA_UINT32, &opts.num_rounds,
|
|
|
|
+ LTC_SSHDATA_EOL)) != CRYPT_OK) {
|
|
|
|
+ die(err);
|
|
|
|
+ }
|
|
|
|
+ if (cur_len != kdfoptionslen) DIE("unused data %lu", kdfoptionslen-cur_len);
|
|
|
|
|
|
- memcpy(privkey, dec_ctx.cur, privkey_len);
|
|
|
|
|
|
+ cur_len = privkey_len;
|
|
|
|
+ if ((err = ssh_decrypt_private_keys(privkey, &cur_len, &opts)) != CRYPT_OK) {
|
|
|
|
+ die(err);
|
|
|
|
+ }
|
|
|
|
+ } else {
|
|
|
|
+ DIE("unsupported kdf %s", kdfname);
|
|
|
|
+ }
|
|
|
|
|
|
- if (memcmp(&privkey[32], pubkey, 32) != 0) DIE("pubkey in privkey doesn't match");
|
|
|
|
|
|
+ pka_key k;
|
|
|
|
|
|
- curve25519_key k;
|
|
|
|
- if ((err = ed25519_set_key(privkey, 32, &privkey[32], 32, &k)) != CRYPT_OK) {
|
|
|
|
|
|
+ cur_len = privkey_len;
|
|
|
|
+ if ((err = ssh_decode_private_key(privkey, &cur_len, &k)) != CRYPT_OK) {
|
|
die(err);
|
|
die(err);
|
|
}
|
|
}
|
|
|
|
|