فهرست منبع

add rsa-support

Signed-off-by: Steffen Jaeckel <[email protected]>
Steffen Jaeckel 6 سال پیش
والد
کامیت
fec3d45adc
1فایلهای تغییر یافته به همراه250 افزوده شده و 109 حذف شده
  1. 250 109
      demos/openssh-privkey.c

+ 250 - 109
demos/openssh-privkey.c

@@ -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);
    }
    }