Browse Source

Add support for more PEM file types + some fixes

Signed-off-by: Steffen Jaeckel <[email protected]>
Steffen Jaeckel 2 years ago
parent
commit
8d19047336

+ 1 - 0
src/headers/tomcrypt_pk.h

@@ -30,6 +30,7 @@ enum ltc_pka_id {
    LTC_PKA_X25519,
    LTC_PKA_X25519,
    LTC_PKA_ED25519,
    LTC_PKA_ED25519,
    LTC_PKA_DH,
    LTC_PKA_DH,
+   LTC_PKA_NUM
 };
 };
 
 
 enum public_key_type {
 enum public_key_type {

+ 4 - 2
src/headers/tomcrypt_private.h

@@ -297,6 +297,7 @@ enum pem_flags {
    pf_encrypted = 0x01u,
    pf_encrypted = 0x01u,
    pf_pkcs8 = 0x02u,
    pf_pkcs8 = 0x02u,
    pf_public = 0x04u,
    pf_public = 0x04u,
+   pf_x509 = 0x08u,
    pf_encrypted_pkcs8 = pf_encrypted | pf_pkcs8,
    pf_encrypted_pkcs8 = pf_encrypted | pf_pkcs8,
 };
 };
 
 
@@ -568,14 +569,15 @@ int x509_decode_public_key_from_certificate(const unsigned char *in, unsigned lo
                                             enum ltc_oid_id algorithm, ltc_asn1_type param_type,
                                             enum ltc_oid_id algorithm, ltc_asn1_type param_type,
                                             ltc_asn1_list* parameters, unsigned long *parameters_len,
                                             ltc_asn1_list* parameters, unsigned long *parameters_len,
                                             public_key_decode_cb callback, void *ctx);
                                             public_key_decode_cb callback, void *ctx);
+int x509_decode_spki(const unsigned char *in, unsigned long inlen, ltc_asn1_list **out, ltc_asn1_list **spki);
 
 
 /* SUBJECT PUBLIC KEY INFO */
 /* SUBJECT PUBLIC KEY INFO */
 int x509_encode_subject_public_key_info(unsigned char *out, unsigned long *outlen,
 int x509_encode_subject_public_key_info(unsigned char *out, unsigned long *outlen,
-        unsigned int algorithm, const void* public_key, unsigned long public_key_len,
+        enum ltc_oid_id algorithm, const void* public_key, unsigned long public_key_len,
         ltc_asn1_type parameters_type, ltc_asn1_list* parameters, unsigned long parameters_len);
         ltc_asn1_type parameters_type, ltc_asn1_list* parameters, unsigned long parameters_len);
 
 
 int x509_decode_subject_public_key_info(const unsigned char *in, unsigned long inlen,
 int x509_decode_subject_public_key_info(const unsigned char *in, unsigned long inlen,
-        unsigned int algorithm, void* public_key, unsigned long* public_key_len,
+        enum ltc_oid_id algorithm, void *public_key, unsigned long *public_key_len,
         ltc_asn1_type parameters_type, ltc_asn1_list* parameters, unsigned long *parameters_len);
         ltc_asn1_type parameters_type, ltc_asn1_list* parameters, unsigned long *parameters_len);
 
 
 int pk_oid_cmp_with_ulong(const char *o1, const unsigned long *o2, unsigned long o2size);
 int pk_oid_cmp_with_ulong(const char *o1, const unsigned long *o2, unsigned long o2size);

+ 17 - 0
src/misc/pem/pem.c

@@ -24,6 +24,13 @@ const struct pem_header_id pem_std_headers[] = {
      .has_more_headers = no,
      .has_more_headers = no,
      .flags = pf_pkcs8,
      .flags = pf_pkcs8,
    },
    },
+   {
+     /* X.509 Certificates */
+     SET_CSTR(.start, "-----BEGIN CERTIFICATE-----"),
+     SET_CSTR(.end, "-----END CERTIFICATE-----"),
+     .has_more_headers = no,
+     .flags = pf_x509,
+   },
    {
    {
      /* Regular (plain) public keys */
      /* Regular (plain) public keys */
      SET_CSTR(.start, "-----BEGIN PUBLIC KEY-----"),
      SET_CSTR(.start, "-----BEGIN PUBLIC KEY-----"),
@@ -31,6 +38,13 @@ const struct pem_header_id pem_std_headers[] = {
      .has_more_headers = no,
      .has_more_headers = no,
      .flags = pf_public,
      .flags = pf_public,
    },
    },
+   {
+     SET_CSTR(.start, "-----BEGIN RSA PUBLIC KEY-----"),
+     SET_CSTR(.end, "-----END RSA PUBLIC KEY-----"),
+     .has_more_headers = no,
+     .pka = LTC_PKA_RSA,
+     .flags = pf_public,
+   },
    /* Regular plain or encrypted private keys */
    /* Regular plain or encrypted private keys */
    {
    {
      SET_CSTR(.start, "-----BEGIN RSA PRIVATE KEY-----"),
      SET_CSTR(.start, "-----BEGIN RSA PRIVATE KEY-----"),
@@ -55,6 +69,9 @@ const unsigned long pem_std_headers_num = sizeof(pem_std_headers)/sizeof(pem_std
 
 
 /* Encrypted PEM files */
 /* Encrypted PEM files */
 const struct str pem_proc_type_encrypted = { SET_CSTR(, "Proc-Type: 4,ENCRYPTED") };
 const struct str pem_proc_type_encrypted = { SET_CSTR(, "Proc-Type: 4,ENCRYPTED") };
+#if defined(LTC_SSH)
+const struct str pem_ssh_comment = { SET_CSTR(, "Comment: ") };
+#endif
 const struct str pem_dek_info_start = { SET_CSTR(, "DEK-Info: ") };
 const struct str pem_dek_info_start = { SET_CSTR(, "DEK-Info: ") };
 const struct blockcipher_info pem_dek_infos[] =
 const struct blockcipher_info pem_dek_infos[] =
    {
    {

+ 107 - 60
src/misc/pem/pem_pkcs.c

@@ -45,6 +45,71 @@ static int s_decrypt_pem(unsigned char *pem, unsigned long *l, const struct pem_
    return err;
    return err;
 }
 }
 
 
+static int s_get_pka(ltc_asn1_list *pub, enum ltc_pka_id *pka)
+{
+   der_flexi_check flexi_should[4];
+   ltc_asn1_list *seqid, *id;
+   enum ltc_oid_id oid_id;
+   int err;
+   unsigned long n = 0;
+   LTC_SET_DER_FLEXI_CHECK(flexi_should, n++, LTC_ASN1_SEQUENCE, &seqid);
+   LTC_SET_DER_FLEXI_CHECK(flexi_should, n++, LTC_ASN1_BIT_STRING, NULL);
+   LTC_SET_DER_FLEXI_CHECK(flexi_should, n, LTC_ASN1_EOL, NULL);
+   if ((err = der_flexi_sequence_cmp(pub, flexi_should)) != CRYPT_OK) {
+      return err;
+   }
+   n = 0;
+   LTC_SET_DER_FLEXI_CHECK(flexi_should, n++, LTC_ASN1_OBJECT_IDENTIFIER, &id);
+   LTC_SET_DER_FLEXI_CHECK(flexi_should, n, LTC_ASN1_EOL, NULL);
+   err = der_flexi_sequence_cmp(seqid, flexi_should);
+   if (err != CRYPT_OK && err != CRYPT_INPUT_TOO_LONG) {
+      return err;
+   }
+   if ((err = pk_get_oid_from_asn1(id, &oid_id)) != CRYPT_OK) {
+      return err;
+   }
+   return pk_get_pka_id(oid_id, pka);
+}
+
+typedef int (*import_fn)(const unsigned char *, unsigned long, void*);
+
+static import_fn s_import_x509_fns[LTC_PKA_NUM] = {
+#ifdef LTC_MRSA
+                                                [LTC_PKA_RSA] = (import_fn)rsa_import_x509,
+#endif
+#ifdef LTC_MECC
+                                                [LTC_PKA_EC] = (import_fn)ecc_import_x509,
+#endif
+#ifdef LTC_CURVE25519
+                                                [LTC_PKA_X25519] = (import_fn)x25519_import_x509,
+                                                [LTC_PKA_ED25519] = (import_fn)ed25519_import_x509,
+#endif
+};
+
+static int s_import_x509(unsigned char *pem, unsigned long l, ltc_pka_key *k)
+{
+   enum ltc_pka_id pka = LTC_PKA_UNDEF;
+   ltc_asn1_list *d, *spki;
+   int err;
+   if ((err = x509_decode_spki(pem, l, &d, &spki)) != CRYPT_OK) {
+      return err;
+   }
+   err = s_get_pka(spki, &pka);
+   der_free_sequence_flexi(d);
+   if (err != CRYPT_OK) {
+      return err;
+   }
+   if (pka < 0
+         || pka > sizeof(s_import_x509_fns)/sizeof(s_import_x509_fns[0])
+         || s_import_x509_fns[pka] == NULL) {
+      return CRYPT_PK_INVALID_TYPE;
+   }
+   if ((err = s_import_x509_fns[pka](pem, l, &k->u)) == CRYPT_OK) {
+      k->id = pka;
+   }
+   return err;
+}
+
 static int s_import_pkcs8(unsigned char *pem, unsigned long l, ltc_pka_key *k, const password_ctx *pw_ctx)
 static int s_import_pkcs8(unsigned char *pem, unsigned long l, ltc_pka_key *k, const password_ctx *pw_ctx)
 {
 {
    int err;
    int err;
@@ -103,6 +168,34 @@ cleanup:
    return err;
    return err;
 }
 }
 
 
+int s_extract_pka(unsigned char *pem, unsigned long w, enum ltc_pka_id *pka)
+{
+   ltc_asn1_list *pub;
+   int err = CRYPT_ERROR;
+   if ((err = der_decode_sequence_flexi(pem, &w, &pub)) != CRYPT_OK) {
+      return err;
+   }
+   err = s_get_pka(pub, pka);
+   der_sequence_free(pub);
+   return err;
+}
+
+static import_fn s_import_openssl_fns[LTC_PKA_NUM] = {
+#ifdef LTC_MRSA
+                                                [LTC_PKA_RSA] = (import_fn)rsa_import,
+#endif
+#ifdef LTC_MDSA
+                                                [LTC_PKA_DSA] = (import_fn)dsa_import,
+#endif
+#ifdef LTC_MECC
+                                                [LTC_PKA_EC] = (import_fn)ecc_import_openssl,
+#endif
+#ifdef LTC_CURVE25519
+                                                [LTC_PKA_X25519] = (import_fn)x25519_import,
+                                                [LTC_PKA_ED25519] = (import_fn)ed25519_import,
+#endif
+};
+
 static int s_decode(struct get_char *g, ltc_pka_key *k, const password_ctx *pw_ctx)
 static int s_decode(struct get_char *g, ltc_pka_key *k, const password_ctx *pw_ctx)
 {
 {
    unsigned char *pem = NULL;
    unsigned char *pem = NULL;
@@ -110,9 +203,6 @@ static int s_decode(struct get_char *g, ltc_pka_key *k, const password_ctx *pw_c
    int err = CRYPT_ERROR;
    int err = CRYPT_ERROR;
    struct pem_headers hdr = { 0 };
    struct pem_headers hdr = { 0 };
    struct password pw;
    struct password pw;
-   ltc_asn1_list *pub = NULL, *seqid, *id;
-   der_flexi_check flexi_should[4];
-   enum ltc_oid_id oid_id;
    enum ltc_pka_id pka;
    enum ltc_pka_id pka;
    XMEMSET(k, 0, sizeof(*k));
    XMEMSET(k, 0, sizeof(*k));
    w = LTC_PEM_READ_BUFSIZE * 2;
    w = LTC_PEM_READ_BUFSIZE * 2;
@@ -135,28 +225,11 @@ retry:
    if (hdr.id->flags & pf_pkcs8) {
    if (hdr.id->flags & pf_pkcs8) {
       err = s_import_pkcs8(pem, l, k, pw_ctx);
       err = s_import_pkcs8(pem, l, k, pw_ctx);
       goto cleanup;
       goto cleanup;
-   } else if (hdr.id->flags & pf_public) {
-      if ((err = der_decode_sequence_flexi(pem, &w, &pub)) != CRYPT_OK) {
-         goto cleanup;
-      }
-      n = 0;
-      LTC_SET_DER_FLEXI_CHECK(flexi_should, n++, LTC_ASN1_SEQUENCE, &seqid);
-      LTC_SET_DER_FLEXI_CHECK(flexi_should, n++, LTC_ASN1_BIT_STRING, NULL);
-      LTC_SET_DER_FLEXI_CHECK(flexi_should, n, LTC_ASN1_EOL, NULL);
-      if ((err = der_flexi_sequence_cmp(pub, flexi_should)) != CRYPT_OK) {
-         goto cleanup;
-      }
-      n = 0;
-      LTC_SET_DER_FLEXI_CHECK(flexi_should, n++, LTC_ASN1_OBJECT_IDENTIFIER, &id);
-      LTC_SET_DER_FLEXI_CHECK(flexi_should, n, LTC_ASN1_EOL, NULL);
-      err = der_flexi_sequence_cmp(seqid, flexi_should);
-      if (err != CRYPT_OK && err != CRYPT_INPUT_TOO_LONG) {
-         goto cleanup;
-      }
-      if ((err = pk_get_oid_from_asn1(id, &oid_id)) != CRYPT_OK) {
-         goto cleanup;
-      }
-      if ((err = pk_get_pka_id(oid_id, &pka)) != CRYPT_OK) {
+   } else if (hdr.id->flags == pf_x509) {
+      err = s_import_x509(pem, l, k);
+      goto cleanup;
+   } else if ((hdr.id->flags & pf_public) && hdr.id->pka == LTC_PKA_UNDEF) {
+      if ((err = s_extract_pka(pem, w, &pka)) != CRYPT_OK) {
          goto cleanup;
          goto cleanup;
       }
       }
    } else if (hdr.encrypted) {
    } else if (hdr.encrypted) {
@@ -178,44 +251,18 @@ retry:
    } else {
    } else {
       pka = hdr.id->pka;
       pka = hdr.id->pka;
    }
    }
-   switch (pka) {
-#ifdef LTC_MDSA
-      case LTC_OID_DSA:
-         err = dsa_import(pem, l, &k->u.dsa);
-         k->id = LTC_PKA_DSA;
-         break;
-#endif
-#ifdef LTC_MRSA
-      case LTC_OID_RSA:
-         err = rsa_import(pem, l, &k->u.rsa);
-         k->id = LTC_PKA_RSA;
-         break;
-#endif
-#ifdef LTC_MECC
-      case LTC_OID_EC:
-         err = ecc_import_openssl(pem, l, &k->u.ecc);
-         k->id = LTC_PKA_EC;
-         break;
-#endif
-#ifdef LTC_CURVE25519
-      case LTC_PKA_ED25519:
-         err = ed25519_import(pem, l, &k->u.ed25519);
-         k->id = LTC_PKA_ED25519;
-         break;
-      case LTC_PKA_X25519:
-         err = x25519_import(pem, l, &k->u.x25519);
-         k->id = LTC_PKA_X25519;
-         break;
-#endif
-      default:
-         err = CRYPT_PK_INVALID_TYPE;
-         goto cleanup;
+
+   if (pka < 0
+         || pka > sizeof(s_import_openssl_fns)/sizeof(s_import_openssl_fns[0])
+         || s_import_openssl_fns[pka] == NULL) {
+      err = CRYPT_PK_INVALID_TYPE;
+      goto cleanup;
+   }
+   if ((err = s_import_openssl_fns[pka](pem, l, &k->u)) == CRYPT_OK) {
+      k->id = pka;
    }
    }
 
 
 cleanup:
 cleanup:
-   if (pub) {
-      der_sequence_free(pub);
-   }
    if (hdr.pw) {
    if (hdr.pw) {
       zeromem(hdr.pw->pw, hdr.pw->l);
       zeromem(hdr.pw->pw, hdr.pw->l);
       XFREE(hdr.pw->pw);
       XFREE(hdr.pw->pw);

+ 7 - 1
src/misc/pem/pem_read.c

@@ -10,6 +10,9 @@
 #ifdef LTC_PEM
 #ifdef LTC_PEM
 
 
 extern const struct str pem_proc_type_encrypted;
 extern const struct str pem_proc_type_encrypted;
+#ifdef LTC_SSH
+extern const struct str pem_ssh_comment;
+#endif
 extern const struct str pem_dek_info_start;
 extern const struct str pem_dek_info_start;
 extern const struct blockcipher_info pem_dek_infos[];
 extern const struct blockcipher_info pem_dek_infos[];
 extern const unsigned long pem_dek_infos_num;
 extern const unsigned long pem_dek_infos_num;
@@ -107,7 +110,10 @@ static int s_pem_decode_headers(struct pem_headers *hdr, struct get_char *g)
       switch (has_more_headers) {
       switch (has_more_headers) {
          case 3:
          case 3:
             if (XMEMCMP(buf, pem_proc_type_encrypted.p, pem_proc_type_encrypted.len)) {
             if (XMEMCMP(buf, pem_proc_type_encrypted.p, pem_proc_type_encrypted.len)) {
-               s_unget_line(buf, slen, g);
+#ifdef LTC_SSH
+               if (XMEMCMP(buf, pem_ssh_comment.p, pem_ssh_comment.len))
+#endif
+                  s_unget_line(buf, slen, g);
                if (hdr->id->has_more_headers == maybe)
                if (hdr->id->has_more_headers == maybe)
                   return CRYPT_OK;
                   return CRYPT_OK;
                else
                else

+ 266 - 87
src/misc/pem/pem_ssh.c

@@ -54,7 +54,7 @@ struct kdf_options {
 };
 };
 
 
 #ifdef LTC_MECC
 #ifdef LTC_MECC
-int ssh_find_init_ecc(const char *pka, ltc_pka_key *key)
+static int s_ssh_find_init_ecc(const char *pka, ltc_pka_key *key)
 {
 {
    int err;
    int err;
    const char* prefix = "ecdsa-sha2-";
    const char* prefix = "ecdsa-sha2-";
@@ -65,54 +65,89 @@ int ssh_find_init_ecc(const char *pka, ltc_pka_key *key)
    return ecc_set_curve(cu, &key->u.ecc);
    return ecc_set_curve(cu, &key->u.ecc);
 }
 }
 
 
-int ssh_decode_ecdsa(const unsigned char *in, unsigned long *inlen, ltc_pka_key *key)
+static int s_ssh_decode_ecdsa(const unsigned char *in, unsigned long *inlen, ltc_pka_key *pka_key, enum pem_flags type)
 {
 {
    int err;
    int err;
-   unsigned char groupname[64], group[512], privkey[512];
-   unsigned long groupnamelen = sizeof(groupname), grouplen = sizeof(group), privkeylen = sizeof(privkey);
+   unsigned char groupname[64], buf0[512], buf1[512];
+   unsigned long groupnamelen = sizeof(groupname), buf0len = sizeof(buf0), buf1len = sizeof(buf1);
+   unsigned long remaining, cur_len, keylen;
+   const unsigned char *p, *key;
 
 
-   if ((err = ssh_decode_sequence_multi(in, inlen,
-                                        LTC_SSHDATA_STRING, groupname, &groupnamelen,
-                                        LTC_SSHDATA_STRING, group, &grouplen,
-                                        LTC_SSHDATA_STRING, privkey, &privkeylen,
-                                        LTC_SSHDATA_EOL,    NULL)) != CRYPT_OK) {
+   p = in;
+   cur_len = *inlen;
+   remaining = *inlen;
+
+   err = ssh_decode_sequence_multi(p, &cur_len,
+                                     LTC_SSHDATA_STRING, groupname, &groupnamelen,
+                                     LTC_SSHDATA_STRING, buf0, &buf0len,
+                                     LTC_SSHDATA_STRING, buf1, &buf1len,
+                                     LTC_SSHDATA_EOL,    NULL);
+   if (err == CRYPT_OK) {
+      key = buf1;
+      keylen = buf1len;
+   } else if (err == CRYPT_BUFFER_OVERFLOW && buf0len != sizeof(buf0) && buf1len == sizeof(buf1)) {
+      key = buf0;
+      keylen = buf0len;
+   } else {
       goto cleanup;
       goto cleanup;
    }
    }
 
 
-   if ((err = ecc_set_key(privkey, privkeylen, PK_PRIVATE, &key->u.ecc)) != CRYPT_OK) {
-      goto cleanup;
-   }
+   remaining -= cur_len;
+   cur_len = remaining;
 
 
-   key->id = LTC_PKA_EC;
+   err = ecc_set_key(key, keylen, type == pf_public ? PK_PUBLIC : PK_PRIVATE, &pka_key->u.ecc);
 
 
 cleanup:
 cleanup:
    zeromem(groupname, sizeof(groupname));
    zeromem(groupname, sizeof(groupname));
-   zeromem(group, sizeof(group));
-   zeromem(privkey, sizeof(privkey));
+   zeromem(buf0, sizeof(buf0));
+   zeromem(buf1, sizeof(buf1));
+   if (err == CRYPT_OK) {
+      pka_key->id = LTC_PKA_EC;
+      *inlen -= remaining;
+   }
 
 
    return err;
    return err;
 }
 }
 #endif
 #endif
 
 
 #ifdef LTC_CURVE25519
 #ifdef LTC_CURVE25519
-int ssh_decode_ed25519(const unsigned char *in, unsigned long *inlen, ltc_pka_key *key)
+static int s_ssh_decode_ed25519(const unsigned char *in, unsigned long *inlen, ltc_pka_key *key, enum pem_flags type)
 {
 {
    int err;
    int err;
-   unsigned char pubkey[2048], privkey[2048];
+   unsigned char pubkey[64], privkey[96];
    unsigned long pubkeylen = sizeof(pubkey), privkeylen = sizeof(privkey);
    unsigned long pubkeylen = sizeof(pubkey), privkeylen = sizeof(privkey);
+   unsigned long remaining, cur_len;
+   const unsigned char *p;
 
 
-   if ((err = ssh_decode_sequence_multi(in, inlen,
+   p = in;
+   cur_len = *inlen;
+   remaining = *inlen;
+
+   if ((err = ssh_decode_sequence_multi(p, &cur_len,
                                         LTC_SSHDATA_STRING, pubkey, &pubkeylen,
                                         LTC_SSHDATA_STRING, pubkey, &pubkeylen,
-                                        LTC_SSHDATA_STRING, privkey, &privkeylen,
                                         LTC_SSHDATA_EOL,    NULL)) != CRYPT_OK) {
                                         LTC_SSHDATA_EOL,    NULL)) != CRYPT_OK) {
       goto cleanup;
       goto cleanup;
    }
    }
 
 
-   if ((err = ed25519_import_raw(&privkey[32], 32, PK_PRIVATE, &key->u.ed25519)) != CRYPT_OK) {
+   if (type == pf_public) {
+      if ((err = ed25519_import_raw(pubkey, pubkeylen, PK_PUBLIC, &key->u.ed25519)) != CRYPT_OK) {
+         goto cleanup;
+      }
+      key->id = LTC_PKA_ED25519;
+      goto cleanup;
+   }
+
+   p += cur_len;
+   remaining -= cur_len;
+   cur_len = remaining;
+
+   if ((err = ssh_decode_sequence_multi(p, &cur_len,
+                                        LTC_SSHDATA_STRING, privkey, &privkeylen,
+                                        LTC_SSHDATA_EOL,    NULL)) != CRYPT_OK) {
       goto cleanup;
       goto cleanup;
    }
    }
-   if (pubkeylen == sizeof(key->u.ed25519.pub)) {
-      XMEMCPY(key->u.ed25519.pub, pubkey, pubkeylen);
+   if ((err = ed25519_import_raw(privkey, privkeylen, PK_PRIVATE, &key->u.ed25519)) != CRYPT_OK) {
+      goto cleanup;
    }
    }
 
 
    key->id = LTC_PKA_ED25519;
    key->id = LTC_PKA_ED25519;
@@ -120,43 +155,150 @@ int ssh_decode_ed25519(const unsigned char *in, unsigned long *inlen, ltc_pka_ke
 cleanup:
 cleanup:
    zeromem(pubkey, sizeof(pubkey));
    zeromem(pubkey, sizeof(pubkey));
    zeromem(privkey, sizeof(privkey));
    zeromem(privkey, sizeof(privkey));
+   if (err == CRYPT_OK) {
+      remaining -= cur_len;
+      *inlen -= remaining;
+   }
 
 
    return err;
    return err;
 }
 }
 #endif
 #endif
 
 
 #ifdef LTC_MRSA
 #ifdef LTC_MRSA
-int ssh_decode_rsa(const unsigned char *in, unsigned long *inlen, ltc_pka_key *key)
+static int s_ssh_decode_dsa(const unsigned char *in, unsigned long *inlen, ltc_pka_key *key, enum pem_flags type)
 {
 {
-   int err;
-   void *tmp1, *tmp2;
-   if ((err = mp_init_multi(&tmp1, &tmp2, NULL)) != CRYPT_OK) {
+   int err, stat;
+   unsigned long remaining, cur_len;
+   const unsigned char *p;
+   if ((err = dsa_int_init(&key->u.dsa)) != CRYPT_OK) {
+      return err;
+   }
+
+   p = in;
+   cur_len = *inlen;
+   remaining = *inlen;
+
+   if ((err = ssh_decode_sequence_multi(p, &cur_len,
+                                        LTC_SSHDATA_MPINT, key->u.dsa.p,
+                                        LTC_SSHDATA_MPINT, key->u.dsa.q,
+                                        LTC_SSHDATA_MPINT, key->u.dsa.g,
+                                        LTC_SSHDATA_MPINT, key->u.dsa.y,
+                                        LTC_SSHDATA_EOL,    NULL)) != CRYPT_OK) {
       goto cleanup;
       goto cleanup;
    }
    }
-   if ((err = rsa_init(&key->u.rsa)) != CRYPT_OK) {
+   key->u.dsa.qord = mp_unsigned_bin_size(key->u.dsa.q);
+   if ((err = dsa_int_validate_pqg(&key->u.dsa, &stat)) != CRYPT_OK) {
+      goto cleanup;
+   }
+   if (stat == 0) {
+      err = CRYPT_INVALID_PACKET;
+      goto cleanup;
+   }
+
+   if (type == pf_public) {
+      key->id = LTC_PKA_DSA;
+      key->u.dsa.type = PK_PUBLIC;
+      goto cleanup;
+   }
+
+   p += cur_len;
+   remaining -= cur_len;
+   cur_len = remaining;
+
+   if ((err = ssh_decode_sequence_multi(p, &cur_len,
+                                        LTC_SSHDATA_MPINT, key->u.dsa.x,
+                                        LTC_SSHDATA_EOL,    NULL)) != CRYPT_OK) {
       goto cleanup;
       goto cleanup;
    }
    }
 
 
-   if ((err = ssh_decode_sequence_multi(in, inlen,
+   key->id = LTC_PKA_DSA;
+   key->u.dsa.type = PK_PRIVATE;
+
+cleanup:
+   if (err != CRYPT_OK) {
+      dsa_free(&key->u.dsa);
+   } else {
+      remaining -= cur_len;
+      *inlen -= remaining;
+   }
+
+   return err;
+
+}
+#endif
+
+#ifdef LTC_MRSA
+static int s_ssh_decode_rsa(const unsigned char *in, unsigned long *inlen, ltc_pka_key *key, enum pem_flags type)
+{
+   int err;
+   void *tmp1, *tmp2;
+   unsigned long remaining, cur_len;
+   const unsigned char *p;
+
+   if ((err = rsa_init(&key->u.rsa)) != CRYPT_OK) {
+      return err;
+   }
+
+   p = in;
+   cur_len = *inlen;
+   remaining = *inlen;
+
+   /* ssh-rsa public and private keys contain `e` and `N` in a different order
+    * public contains `e`, then `N`
+    * private contains `N`, then `e`
+    * change the order later on if we import a public key */
+   if ((err = ssh_decode_sequence_multi(p, &cur_len,
                                         LTC_SSHDATA_MPINT, key->u.rsa.N,
                                         LTC_SSHDATA_MPINT, key->u.rsa.N,
                                         LTC_SSHDATA_MPINT, key->u.rsa.e,
                                         LTC_SSHDATA_MPINT, key->u.rsa.e,
+                                        LTC_SSHDATA_EOL,    NULL)) != CRYPT_OK) {
+      goto cleanup;
+   }
+
+   p += cur_len;
+   remaining -= cur_len;
+   cur_len = remaining;
+
+   if (type == pf_public) {
+      /* c.f. comment above */
+      void *exch = key->u.rsa.N;
+      key->u.rsa.N = key->u.rsa.e;
+      key->u.rsa.e = exch;
+      key->id = LTC_PKA_RSA;
+      key->u.rsa.type = PK_PUBLIC;
+      *inlen -= remaining;
+      goto cleanup;
+   }
+
+   if ((err = mp_init_multi(&tmp1, &tmp2, NULL)) != CRYPT_OK) {
+      goto cleanup;
+   }
+
+   if ((err = ssh_decode_sequence_multi(p, &cur_len,
                                         LTC_SSHDATA_MPINT, key->u.rsa.d,
                                         LTC_SSHDATA_MPINT, key->u.rsa.d,
                                         LTC_SSHDATA_MPINT, key->u.rsa.qP,
                                         LTC_SSHDATA_MPINT, key->u.rsa.qP,
-                                        LTC_SSHDATA_MPINT, key->u.rsa.q,
                                         LTC_SSHDATA_MPINT, key->u.rsa.p,
                                         LTC_SSHDATA_MPINT, key->u.rsa.p,
+                                        LTC_SSHDATA_MPINT, key->u.rsa.q,
                                         LTC_SSHDATA_EOL,    NULL)) != CRYPT_OK) {
                                         LTC_SSHDATA_EOL,    NULL)) != CRYPT_OK) {
-      goto cleanup;
+      goto cleanup_tmps;
    }
    }
 
 
-   if ((err = mp_sub_d(key->u.rsa.p, 1,  tmp1)) != CRYPT_OK)                     { goto cleanup; } /* tmp1 = q-1 */
-   if ((err = mp_sub_d(key->u.rsa.q, 1,  tmp2)) != CRYPT_OK)                     { goto cleanup; } /* tmp2 = p-1 */
-   if ((err = mp_mod( key->u.rsa.d,  tmp1,  key->u.rsa.dP)) != CRYPT_OK)         { goto cleanup; } /* dP = d mod p-1 */
-   if ((err = mp_mod( key->u.rsa.d,  tmp2,  key->u.rsa.dQ)) != CRYPT_OK)         { goto cleanup; } /* dQ = d mod q-1 */
+   if ((err = mp_sub_d(key->u.rsa.p, 1, tmp1)) != CRYPT_OK)                   { goto cleanup_tmps; } /* tmp1 = q-1 */
+   if ((err = mp_sub_d(key->u.rsa.q, 1, tmp2)) != CRYPT_OK)                   { goto cleanup_tmps; } /* tmp2 = p-1 */
+   if ((err = mp_mod(key->u.rsa.d, tmp1, key->u.rsa.dP)) != CRYPT_OK)         { goto cleanup_tmps; } /* dP = d mod p-1 */
+   if ((err = mp_mod(key->u.rsa.d, tmp2, key->u.rsa.dQ)) != CRYPT_OK)         { goto cleanup_tmps; } /* dQ = d mod q-1 */
 
 
    key->id = LTC_PKA_RSA;
    key->id = LTC_PKA_RSA;
+   key->u.rsa.type = PK_PRIVATE;
 
 
-cleanup:
+cleanup_tmps:
    mp_clear_multi(tmp2, tmp1, NULL);
    mp_clear_multi(tmp2, tmp1, NULL);
+cleanup:
+   if (err != CRYPT_OK) {
+      rsa_free(&key->u.rsa);
+   } else {
+      remaining -= cur_len;
+      *inlen -= remaining;
+   }
 
 
    return err;
    return err;
 }
 }
@@ -165,22 +307,25 @@ cleanup:
 struct ssh_pka {
 struct ssh_pka {
    const char *name;
    const char *name;
    int (*init)(const char*, ltc_pka_key*);
    int (*init)(const char*, ltc_pka_key*);
-   int (*decode)(const unsigned char*, unsigned long*, ltc_pka_key*);
+   int (*decode)(const unsigned char*, unsigned long*, ltc_pka_key*, enum pem_flags);
 };
 };
 
 
 struct ssh_pka ssh_pkas[] = {
 struct ssh_pka ssh_pkas[] = {
 #ifdef LTC_CURVE25519
 #ifdef LTC_CURVE25519
-                             { "ssh-ed25519", NULL,              ssh_decode_ed25519 },
+                             { "ssh-ed25519", NULL,                s_ssh_decode_ed25519 },
 #endif
 #endif
 #ifdef LTC_MRSA
 #ifdef LTC_MRSA
-                             { "ssh-rsa",     NULL,              ssh_decode_rsa },
+                             { "ssh-rsa",     NULL,                s_ssh_decode_rsa },
+#endif
+#ifdef LTC_MDSA
+                             { "ssh-dss",     NULL,                s_ssh_decode_dsa },
 #endif
 #endif
 #ifdef LTC_MECC
 #ifdef LTC_MECC
-                             { NULL,          ssh_find_init_ecc, ssh_decode_ecdsa },
+                             { NULL,          s_ssh_find_init_ecc, s_ssh_decode_ecdsa },
 #endif
 #endif
 };
 };
 
 
-static int s_decode_private_key(const unsigned char *in, unsigned long *inlen, ltc_pka_key *key)
+static int s_decode_key(const unsigned char *in, unsigned long *inlen, ltc_pka_key *key, enum pem_flags type)
 {
 {
    int err;
    int err;
    ulong32 check1, check2;
    ulong32 check1, check2;
@@ -196,20 +341,31 @@ static int s_decode_private_key(const unsigned char *in, unsigned long *inlen, l
 
 
    p = in;
    p = in;
    cur_len = *inlen;
    cur_len = *inlen;
+   remaining = *inlen;
 
 
+   if (type != pf_public) {
+      if ((err = ssh_decode_sequence_multi(p, &cur_len,
+                                           LTC_SSHDATA_UINT32, &check1,
+                                           LTC_SSHDATA_UINT32, &check2,
+                                           LTC_SSHDATA_EOL,    NULL)) != CRYPT_OK) {
+         return err;
+      }
+      if (check1 != check2) {
+         return CRYPT_INVALID_PACKET;
+      }
+
+      p += cur_len;
+      remaining -= cur_len;
+      cur_len = remaining;
+   }
    if ((err = ssh_decode_sequence_multi(p, &cur_len,
    if ((err = ssh_decode_sequence_multi(p, &cur_len,
-                                        LTC_SSHDATA_UINT32, &check1,
-                                        LTC_SSHDATA_UINT32, &check2,
                                         LTC_SSHDATA_STRING, pka, &pkalen,
                                         LTC_SSHDATA_STRING, pka, &pkalen,
                                         LTC_SSHDATA_EOL,    NULL)) != CRYPT_OK) {
                                         LTC_SSHDATA_EOL,    NULL)) != CRYPT_OK) {
       return err;
       return err;
    }
    }
-   if (check1 != check2) {
-      return CRYPT_INVALID_PACKET;
-   }
 
 
    p += cur_len;
    p += cur_len;
-   remaining = *inlen - cur_len;
+   remaining -= cur_len;
    cur_len = remaining;
    cur_len = remaining;
 
 
    for (n = 0; n < sizeof(ssh_pkas)/sizeof(ssh_pkas[0]); ++n) {
    for (n = 0; n < sizeof(ssh_pkas)/sizeof(ssh_pkas[0]); ++n) {
@@ -219,7 +375,7 @@ static int s_decode_private_key(const unsigned char *in, unsigned long *inlen, l
          if ((ssh_pkas[n].init == NULL) ||
          if ((ssh_pkas[n].init == NULL) ||
                (ssh_pkas[n].init((char*)pka, key) != CRYPT_OK)) continue;
                (ssh_pkas[n].init((char*)pka, key) != CRYPT_OK)) continue;
       }
       }
-      if ((err = ssh_pkas[n].decode(p, &cur_len, key)) != CRYPT_OK) {
+      if ((err = ssh_pkas[n].decode(p, &cur_len, key, type)) != CRYPT_OK) {
          return err;
          return err;
       }
       }
       break;
       break;
@@ -232,10 +388,12 @@ static int s_decode_private_key(const unsigned char *in, unsigned long *inlen, l
    remaining -= cur_len;
    remaining -= cur_len;
    cur_len = remaining;
    cur_len = remaining;
 
 
-   if ((err = ssh_decode_sequence_multi(p, &cur_len,
-                                        LTC_SSHDATA_STRING, comment, &commentlen,
-                                        LTC_SSHDATA_EOL,    NULL)) != CRYPT_OK) {
-      return err;
+   if (cur_len != 0) {
+      if ((err = ssh_decode_sequence_multi(p, &cur_len,
+                                           LTC_SSHDATA_STRING, comment, &commentlen,
+                                           LTC_SSHDATA_EOL,    NULL)) != CRYPT_OK) {
+         return err;
+      }
    }
    }
 
 
    p += cur_len;
    p += cur_len;
@@ -345,65 +503,86 @@ static int s_decode_header(unsigned char *in, unsigned long *inlen, struct kdf_o
    return err;
    return err;
 }
 }
 
 
-static const struct pem_header_id pem_openssh =
+
+static const struct pem_header_id pem_openssh[] = {
    {
    {
      SET_CSTR(.start, "-----BEGIN OPENSSH PRIVATE KEY-----"),
      SET_CSTR(.start, "-----BEGIN OPENSSH PRIVATE KEY-----"),
      SET_CSTR(.end, "-----END OPENSSH PRIVATE KEY-----"),
      SET_CSTR(.end, "-----END OPENSSH PRIVATE KEY-----"),
-     .has_more_headers = 0
-   };
+     .has_more_headers = no
+   },
+   {
+     SET_CSTR(.start, "---- BEGIN SSH2 PUBLIC KEY ----"),
+     SET_CSTR(.end, "---- END SSH2 PUBLIC KEY ----"),
+     .has_more_headers = maybe,
+     .flags = pf_public
+   },
+};
+static const unsigned long pem_openssh_num = sizeof(pem_openssh)/sizeof(pem_openssh[0]);
 
 
 static int s_decode_openssh(struct get_char *g, ltc_pka_key *k, const password_ctx *pw_ctx)
 static int s_decode_openssh(struct get_char *g, ltc_pka_key *k, const password_ctx *pw_ctx)
 {
 {
    unsigned char *pem = NULL, *p, *privkey = NULL;
    unsigned char *pem = NULL, *p, *privkey = NULL;
-   unsigned long w, l, privkey_len;
+   unsigned long n, w, l, privkey_len;
    int err;
    int err;
-   struct pem_headers hdr = { .id = &pem_openssh };
+   struct pem_headers hdr;
    struct kdf_options opts = { 0 };
    struct kdf_options opts = { 0 };
    XMEMSET(k, 0, sizeof(*k));
    XMEMSET(k, 0, sizeof(*k));
    w = LTC_PEM_READ_BUFSIZE * 2;
    w = LTC_PEM_READ_BUFSIZE * 2;
 retry:
 retry:
    pem = XREALLOC(pem, w);
    pem = XREALLOC(pem, w);
-   err = pem_read(pem, &w, &hdr, g);
-   if (err == CRYPT_BUFFER_OVERFLOW) {
-      goto retry;
-   } else if (err != CRYPT_OK) {
-      goto cleanup;
+   for (n = 0; n < pem_openssh_num; ++n) {
+      hdr.id = &pem_openssh[n];
+      err = pem_read(pem, &w, &hdr, g);
+      if (err == CRYPT_BUFFER_OVERFLOW) {
+         goto retry;
+      } else if (err == CRYPT_OK) {
+         break;
+      }
+      hdr.id = NULL;
    }
    }
-   l = w;
-   if ((err = s_decode_header(pem, &w, &opts)) != CRYPT_OK) {
+   /* id not found */
+   if (hdr.id == NULL) {
       goto cleanup;
       goto cleanup;
    }
    }
-   p = pem + w;
-   l -= w;
-   w = l;
-
-   privkey_len = l;
-   privkey = XMALLOC(privkey_len);
+   p = pem;
+   l = w;
+   if (hdr.id->flags != pf_public) {
+      if ((err = s_decode_header(pem, &w, &opts)) != CRYPT_OK) {
+         goto cleanup;
+      }
+      p = pem + w;
+      l -= w;
+      w = l;
 
 
-   if ((err = ssh_decode_sequence_multi(p, &w,
-                                        LTC_SSHDATA_STRING, privkey, &privkey_len,
-                                        LTC_SSHDATA_EOL,    NULL)) != CRYPT_OK) {
-      goto cleanup;
-   }
+      privkey_len = l;
+      privkey = XMALLOC(privkey_len);
 
 
-   if (XSTRCMP(opts.name, "none") != 0) {
-      if ((pw_ctx == NULL) || (pw_ctx->callback == NULL)) {
-         err = CRYPT_PW_CTX_MISSING;
+      if ((err = ssh_decode_sequence_multi(p, &w,
+                                           LTC_SSHDATA_STRING, privkey, &privkey_len,
+                                           LTC_SSHDATA_EOL,    NULL)) != CRYPT_OK) {
          goto cleanup;
          goto cleanup;
       }
       }
-      if (pw_ctx->callback(&opts.pw.pw, &opts.pw.l, pw_ctx->userdata)) {
-         err = CRYPT_ERROR;
-         goto cleanup;
+
+      if (XSTRCMP(opts.name, "none") != 0) {
+         if ((pw_ctx == NULL) || (pw_ctx->callback == NULL)) {
+            err = CRYPT_PW_CTX_MISSING;
+            goto cleanup;
+         }
+         if (pw_ctx->callback(&opts.pw.pw, &opts.pw.l, pw_ctx->userdata)) {
+            err = CRYPT_ERROR;
+            goto cleanup;
+         }
+         w = privkey_len;
+         if ((err = s_decrypt_private_keys(privkey, &privkey_len, &opts)) != CRYPT_OK) {
+            goto cleanup;
+         }
+         zeromem(opts.pw.pw, opts.pw.l);
       }
       }
+
+      p = privkey;
       w = privkey_len;
       w = privkey_len;
-      if ((err = s_decrypt_private_keys(privkey, &privkey_len, &opts)) != CRYPT_OK) {
-         goto cleanup;
-      }
-      zeromem(opts.pw.pw, opts.pw.l);
    }
    }
-
-   w = privkey_len;
-   if ((err = s_decode_private_key(privkey, &w, k)) != CRYPT_OK) {
+   if ((err = s_decode_key(p, &w, k, hdr.id->flags)) != CRYPT_OK) {
       goto cleanup;
       goto cleanup;
    }
    }
 
 

+ 23 - 61
src/pk/asn1/x509/x509_decode_public_key_from_certificate.c

@@ -9,14 +9,6 @@
 
 
 #ifdef LTC_DER
 #ifdef LTC_DER
 
 
-/* Check if it looks like a SubjectPublicKeyInfo */
-#define LOOKS_LIKE_SPKI(l) ((l) != NULL)              \
-&& ((l)->type == LTC_ASN1_SEQUENCE)                   \
-&& ((l)->child != NULL)                               \
-&& ((l)->child->type == LTC_ASN1_OBJECT_IDENTIFIER)   \
-&& ((l)->next != NULL)                                \
-&& ((l)->next->type == LTC_ASN1_BIT_STRING)
-
 /**
 /**
   Try to decode the public key from a X.509 certificate
   Try to decode the public key from a X.509 certificate
    @param in               The input buffer
    @param in               The input buffer
@@ -27,7 +19,9 @@
    @param parameters_len   [in/out] The number of parameters to include
    @param parameters_len   [in/out] The number of parameters to include
    @param callback         The callback
    @param callback         The callback
    @param ctx              The context passed to the callback
    @param ctx              The context passed to the callback
-   @return CRYPT_OK on success, CRYPT_NOP if no SubjectPublicKeyInfo was found
+   @return CRYPT_OK on success,
+            CRYPT_NOP if no SubjectPublicKeyInfo was found,
+            another error if decoding or memory allocation failed
 */
 */
 int x509_decode_public_key_from_certificate(const unsigned char *in, unsigned long inlen,
 int x509_decode_public_key_from_certificate(const unsigned char *in, unsigned long inlen,
                                             enum ltc_oid_id algorithm, ltc_asn1_type param_type,
                                             enum ltc_oid_id algorithm, ltc_asn1_type param_type,
@@ -35,67 +29,35 @@ int x509_decode_public_key_from_certificate(const unsigned char *in, unsigned lo
                                             public_key_decode_cb callback, void *ctx)
                                             public_key_decode_cb callback, void *ctx)
 {
 {
    int err;
    int err;
-   unsigned char *tmpbuf;
-   unsigned long tmpbuf_len, tmp_inlen;
-   ltc_asn1_list *decoded_list = NULL, *l;
+   unsigned char *tmpbuf = NULL;
+   unsigned long tmpbuf_len;
+   ltc_asn1_list *decoded_list = NULL, *spki;
 
 
    LTC_ARGCHK(in       != NULL);
    LTC_ARGCHK(in       != NULL);
    LTC_ARGCHK(inlen    != 0);
    LTC_ARGCHK(inlen    != 0);
    LTC_ARGCHK(callback != NULL);
    LTC_ARGCHK(callback != NULL);
 
 
-   tmpbuf_len = inlen;
-   tmpbuf = XCALLOC(1, tmpbuf_len);
-   if (tmpbuf == NULL) {
-       err = CRYPT_MEM;
-       goto LBL_OUT;
+   if ((err = x509_decode_spki(in, inlen, &decoded_list, &spki)) != CRYPT_OK) {
+      return err;
    }
    }
 
 
-   tmp_inlen = inlen;
-   if ((err = der_decode_sequence_flexi(in, &tmp_inlen, &decoded_list)) == CRYPT_OK) {
-      l = decoded_list;
-
-      err = CRYPT_NOP;
+   if (algorithm == LTC_OID_EC) {
+      err = callback(spki->data, spki->size, ctx);
+   } else {
 
 
-      /* Move 2 levels up in the tree
-         SEQUENCE
-             SEQUENCE
-                 ...
-       */
-      if ((l->type == LTC_ASN1_SEQUENCE) && (l->child != NULL)) {
-         l = l->child;
-         if ((l->type == LTC_ASN1_SEQUENCE) && (l->child != NULL)) {
-            l = l->child;
+      tmpbuf_len = inlen;
+      tmpbuf = XCALLOC(1, tmpbuf_len);
+      if (tmpbuf == NULL) {
+          err = CRYPT_MEM;
+          goto LBL_OUT;
+      }
 
 
-            /* Move forward in the tree until we find this combination
-                 ...
-                 SEQUENCE
-                     SEQUENCE
-                         OBJECT IDENTIFIER <some PKA OID, e.g. 1.2.840.113549.1.1.1>
-                         NULL
-                     BIT STRING
-             */
-            do {
-               /* The additional check for l->data is there to make sure
-                * we won't try to decode a list that has been 'shrunk'
-                */
-               if ((l->type == LTC_ASN1_SEQUENCE)
-                     && (l->data != NULL)
-                     && LOOKS_LIKE_SPKI(l->child)) {
-                  if (algorithm == LTC_OID_EC) {
-                     err = callback(l->data, l->size, ctx);
-                  } else {
-                     err = x509_decode_subject_public_key_info(l->data, l->size,
-                                                               algorithm, tmpbuf, &tmpbuf_len,
-                                                               param_type, parameters, parameters_len);
-                     if (err == CRYPT_OK) {
-                        err = callback(tmpbuf, tmpbuf_len, ctx);
-                        goto LBL_OUT;
-                     }
-                  }
-               }
-               l = l->next;
-            } while(l);
-         }
+      err = x509_decode_subject_public_key_info(spki->data, spki->size,
+                                                algorithm, tmpbuf, &tmpbuf_len,
+                                                param_type, parameters, parameters_len);
+      if (err == CRYPT_OK) {
+         err = callback(tmpbuf, tmpbuf_len, ctx);
+         goto LBL_OUT;
       }
       }
    }
    }
 
 

+ 82 - 0
src/pk/asn1/x509/x509_decode_spki.c

@@ -0,0 +1,82 @@
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis */
+/* SPDX-License-Identifier: Unlicense */
+#include "tomcrypt_private.h"
+
+/**
+  @file x509_decode_public_key_from_certificate.c
+  ASN.1 DER/X.509, decode a SubjectPublicKeyInfo
+*/
+
+#ifdef LTC_DER
+
+/* Check if it looks like a SubjectPublicKeyInfo */
+#define LOOKS_LIKE_SPKI(l) ((l) != NULL)              \
+&& ((l)->type == LTC_ASN1_SEQUENCE)                   \
+&& ((l)->child != NULL)                               \
+&& ((l)->child->type == LTC_ASN1_OBJECT_IDENTIFIER)   \
+&& ((l)->next != NULL)                                \
+&& ((l)->next->type == LTC_ASN1_BIT_STRING)
+
+/**
+  DER decode a X.509 certificate and return the SubjectPublicKeyInfo
+   @param in               The input buffer
+   @param inlen            The length of the input buffer
+   @param out              [out] A pointer to the decoded linked list (you take ownership of this one and
+                                 `der_free_sequence_flexi()` it when you're done)
+   @param spki             [out] A pointer to the SubjectPublicKeyInfo
+   @return CRYPT_OK on success, CRYPT_NOP if no SubjectPublicKeyInfo was found, another error if decoding failed
+*/
+int x509_decode_spki(const unsigned char *in, unsigned long inlen, ltc_asn1_list **out, ltc_asn1_list **spki)
+{
+   int err;
+   unsigned long tmp_inlen;
+   ltc_asn1_list *decoded_list = NULL, *l;
+
+   LTC_ARGCHK(in       != NULL);
+   LTC_ARGCHK(inlen    != 0);
+
+   tmp_inlen = inlen;
+   if ((err = der_decode_sequence_flexi(in, &tmp_inlen, &decoded_list)) == CRYPT_OK) {
+      l = decoded_list;
+
+      err = CRYPT_NOP;
+
+      /* Move 2 levels up in the tree
+         SEQUENCE
+             SEQUENCE
+                 ...
+       */
+      if ((l->type == LTC_ASN1_SEQUENCE) && (l->child != NULL)) {
+         l = l->child;
+         if ((l->type == LTC_ASN1_SEQUENCE) && (l->child != NULL)) {
+            l = l->child;
+
+            /* Move forward in the tree until we find this combination
+                 ...
+                 SEQUENCE
+                     SEQUENCE
+                         OBJECT IDENTIFIER <some PKA OID, e.g. 1.2.840.113549.1.1.1>
+                         NULL
+                     BIT STRING
+             */
+            do {
+               /* The additional check for l->data is there to make sure
+                * we won't try to decode a list that has been 'shrunk'
+                */
+               if ((l->type == LTC_ASN1_SEQUENCE)
+                     && (l->data != NULL)
+                     && LOOKS_LIKE_SPKI(l->child)) {
+                  *out = decoded_list;
+                  *spki = l;
+                  return CRYPT_OK;
+               }
+               l = l->next;
+            } while(l);
+         }
+      }
+   }
+   if (decoded_list) der_free_sequence_flexi(decoded_list);
+   return err;
+}
+
+#endif

+ 1 - 1
src/pk/asn1/x509/x509_decode_subject_public_key_info.c

@@ -32,7 +32,7 @@
    @return CRYPT_OK on success
    @return CRYPT_OK on success
 */
 */
 int x509_decode_subject_public_key_info(const unsigned char *in, unsigned long inlen,
 int x509_decode_subject_public_key_info(const unsigned char *in, unsigned long inlen,
-        unsigned int algorithm, void* public_key, unsigned long* public_key_len,
+        enum ltc_oid_id algorithm, void *public_key, unsigned long *public_key_len,
         ltc_asn1_type parameters_type, ltc_asn1_list* parameters, unsigned long *parameters_len)
         ltc_asn1_type parameters_type, ltc_asn1_list* parameters, unsigned long *parameters_len)
 {
 {
    int err;
    int err;

+ 1 - 1
src/pk/asn1/x509/x509_encode_subject_public_key_info.c

@@ -32,7 +32,7 @@
    @return CRYPT_OK on success
    @return CRYPT_OK on success
 */
 */
 int x509_encode_subject_public_key_info(unsigned char *out, unsigned long *outlen,
 int x509_encode_subject_public_key_info(unsigned char *out, unsigned long *outlen,
-        unsigned int algorithm, const void* public_key, unsigned long public_key_len,
+        enum ltc_oid_id algorithm, const void* public_key, unsigned long public_key_len,
         ltc_asn1_type parameters_type, ltc_asn1_list* parameters, unsigned long parameters_len)
         ltc_asn1_type parameters_type, ltc_asn1_list* parameters, unsigned long parameters_len)
 {
 {
    int           err;
    int           err;

+ 7 - 2
src/pk/ed25519/ed25519_import_raw.c

@@ -21,13 +21,18 @@
 int ed25519_import_raw(const unsigned char *in, unsigned long inlen, int which, curve25519_key *key)
 int ed25519_import_raw(const unsigned char *in, unsigned long inlen, int which, curve25519_key *key)
 {
 {
    LTC_ARGCHK(in   != NULL);
    LTC_ARGCHK(in   != NULL);
-   LTC_ARGCHK(inlen == 32uL);
    LTC_ARGCHK(key  != NULL);
    LTC_ARGCHK(key  != NULL);
 
 
    if (which == PK_PRIVATE) {
    if (which == PK_PRIVATE) {
+      LTC_ARGCHK(inlen == 32uL || inlen == 64uL);
       XMEMCPY(key->priv, in, sizeof(key->priv));
       XMEMCPY(key->priv, in, sizeof(key->priv));
-      tweetnacl_crypto_sk_to_pk(key->pub, key->priv);
+      if (inlen == 32) {
+         tweetnacl_crypto_sk_to_pk(key->pub, key->priv);
+      } else {
+         XMEMCPY(key->pub, in + 32, sizeof(key->pub));
+      }
    } else if (which == PK_PUBLIC) {
    } else if (which == PK_PUBLIC) {
+      LTC_ARGCHK(inlen == 32uL);
       XMEMCPY(key->pub, in, sizeof(key->pub));
       XMEMCPY(key->pub, in, sizeof(key->pub));
    } else {
    } else {
       return CRYPT_INVALID_ARG;
       return CRYPT_INVALID_ARG;

+ 2 - 2
src/pk/rsa/rsa_import_x509.c

@@ -39,8 +39,8 @@ int rsa_import_x509(const unsigned char *in, unsigned long inlen, rsa_key *key)
    }
    }
 
 
    if ((err = x509_decode_public_key_from_certificate(in, inlen,
    if ((err = x509_decode_public_key_from_certificate(in, inlen,
-                                                      LTC_OID_RSA, LTC_ASN1_NULL,
-                                                      NULL, NULL,
+                                                      LTC_OID_RSA,
+                                                      LTC_ASN1_NULL, NULL, NULL,
                                                       (public_key_decode_cb)s_rsa_decode, key)) != CRYPT_OK) {
                                                       (public_key_decode_cb)s_rsa_decode, key)) != CRYPT_OK) {
       rsa_free(key);
       rsa_free(key);
    } else {
    } else {

+ 9 - 0
tests/pem/extra/ed25519-cryptx.pem

@@ -0,0 +1,9 @@
+-----BEGIN CERTIFICATE-----
+MIIBODCB66ADAgECAhRWDU9FZBBUZ7KTdX8f7Bco8jsoaTAFBgMrZXAwETEPMA0G
+A1UEAwwGQ3J5cHRYMCAXDTIwMDExOTEzMDIwMloYDzIyOTMxMTAyMTMwMjAyWjAR
+MQ8wDQYDVQQDDAZDcnlwdFgwKjAFBgMrZXADIQCgXRrqWDCsmmXN+zhGYNSX42l8
+RrQZzyzshd6L0kVFnaNTMFEwHQYDVR0OBBYEFHCGFtVibAxxWYyRt5wazMpqSZDV
+MB8GA1UdIwQYMBaAFHCGFtVibAxxWYyRt5wazMpqSZDVMA8GA1UdEwEB/wQFMAMB
+Af8wBQYDK2VwA0EAqG/+98smzqF/wmFX3zHXSaA67as202HnBJod1Tiurw1f+lr3
+BX6OMtsDpgRq9O77IF1Qyx/MdJEwwErczOIbAA==
+-----END CERTIFICATE-----

+ 11 - 0
tests/pem/extra/ed25519-selfsigned.pem

@@ -0,0 +1,11 @@
+-----BEGIN CERTIFICATE-----
+MIIBhDCCATagAwIBAgIUHDa8kJUZeCuIhknPJ/wDeBW6gZgwBQYDK2VwMDgxNjA0
+BgNVBAMMLVB1YmxpYyBkb21haW4gZWQyNTUxOSBzZWxmLXNpZ25lZCBjZXJ0aWZp
+Y2F0ZTAgFw0xOTA1MjMyMTAxMTZaGA8yMTE5MDUyNDIxMDExNlowODE2MDQGA1UE
+AwwtUHVibGljIGRvbWFpbiBlZDI1NTE5IHNlbGYtc2lnbmVkIGNlcnRpZmljYXRl
+MCowBQYDK2VwAyEAUEiKvHT0KHXOtNjIhaImokxbiog+Ki6lcgce05tf9UKjUDBO
+MB0GA1UdDgQWBBS3fmpWaPK2fNpblEmg4tG4ZHO2BDAfBgNVHSMEGDAWgBS3fmpW
+aPK2fNpblEmg4tG4ZHO2BDAMBgNVHRMEBTADAQH/MAUGAytlcANBADOnwkj8etmx
+mTaXUP29RaenxpN8dQoQ4wnnIJwxQxTcVWOt2PlUxCFoB9gs0+YZOzhXnQg4hfqk
+t/HPExwoZQg=
+-----END CERTIFICATE-----

+ 1 - 0
tests/pem_test.c

@@ -126,6 +126,7 @@ int pem_test(void)
 #ifdef LTC_SSH
 #ifdef LTC_SSH
    DO(test_process_dir("tests/ssh", &key, s_pem_decode_ssh, NULL, (dir_cleanup_cb)pka_key_free, "pem_test+ssh"));
    DO(test_process_dir("tests/ssh", &key, s_pem_decode_ssh, NULL, (dir_cleanup_cb)pka_key_free, "pem_test+ssh"));
    DO(test_process_dir("tests/ssh", &key, NULL, s_pem_decode_ssh_f, (dir_cleanup_cb)pka_key_free, "pem_test_filehandle+ssh"));
    DO(test_process_dir("tests/ssh", &key, NULL, s_pem_decode_ssh_f, (dir_cleanup_cb)pka_key_free, "pem_test_filehandle+ssh"));
+   DO(test_process_dir("tests/ssh/extra", &key, s_pem_decode_ssh, NULL, (dir_cleanup_cb)pka_key_free, "pem_test+ssh+extra"));
 #endif
 #endif
 
 
 #if defined(LTC_MDSA)
 #if defined(LTC_MDSA)

+ 21 - 0
tests/ssh/extra/dsa-private.pem

@@ -0,0 +1,21 @@
+-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABsQAAAAdzc2gtZH
+NzAAAAgQClPP2r5pBXhp0qsGBu3WZ0JRvtNUDWsbtxeb9DXC/0kVFuyHaVKvLdeLIitJgO
+so6YS4Tn+bfILoExFQZZT/OgDUmxYoB+1jd7+snSVqputKTYTRzf/+dHJzbSLF28Xqt1bb
+COuKZB9TianmQxy5ru95OE9BCjs7MpUnxf8LVQSQAAABUAoabmyx1YsDwfo0r1G/HuEx0u
+zwUAAACACQ9TzAokwkGtccg/zQljmowrn0ziMygIZupfp5QVT4iiPtutl6WLdduynuJjy/
+FyQYs6E40kDdPLhzIP/C+lv3HTtmmfpoZAZ0tcQJvNwwMKi6w62kdcP+EERca+VW8svKp3
+o6z40yaGwTdQRrL/OMB5I5qAp+qRSH5BmHgE5SYAAACAB2D1NczFQUw1q7u0jQuBtlMlhl
+mGC4y8rQVR30JgWudQpqq0rNpAnxixgNcp32BDbMXCavZ7F62+Itex+QRyRZB9IOwVA6Xg
+Xi6/ILCt1oH6lsLWbA5JbTm8PXIVA/7Iiuqv3ZP30iAdh6NIp3r+5OvGeWZDSOFzsLhLLA
+FYZsAAAAHwKUeNkClHjZAAAAAHc3NoLWRzcwAAAIEApTz9q+aQV4adKrBgbt1mdCUb7TVA
+1rG7cXm/Q1wv9JFRbsh2lSry3XiyIrSYDrKOmEuE5/m3yC6BMRUGWU/zoA1JsWKAftY3e/
+rJ0laqbrSk2E0c3//nRyc20ixdvF6rdW2wjrimQfU4mp5kMcua7veThPQQo7OzKVJ8X/C1
+UEkAAAAVAKGm5ssdWLA8H6NK9Rvx7hMdLs8FAAAAgAkPU8wKJMJBrXHIP80JY5qMK59M4j
+MoCGbqX6eUFU+Ioj7brZeli3Xbsp7iY8vxckGLOhONJA3Ty4cyD/wvpb9x07Zpn6aGQGdL
+XECbzcMDCousOtpHXD/hBEXGvlVvLLyqd6Os+NMmhsE3UEay/zjAeSOagKfqkUh+QZh4BO
+UmAAAAgAdg9TXMxUFMNau7tI0LgbZTJYZZhguMvK0FUd9CYFrnUKaqtKzaQJ8YsYDXKd9g
+Q2zFwmr2exetviLXsfkEckWQfSDsFQOl4F4uvyCwrdaB+pbC1mwOSW05vD1yFQP+yIrqr9
+2T99IgHYejSKd6/uTrxnlmQ0jhc7C4SywBWGbAAAAAFQCYwb2dlv1ktqrIWBTO+Hn+CJ4w
+RAAAABNUaGlzIGlzIGEgdGVzdCBrZXkhAQIDBAUGBw==
+-----END OPENSSH PRIVATE KEY-----

+ 12 - 0
tests/ssh/extra/dsa-public.pem

@@ -0,0 +1,12 @@
+---- BEGIN SSH2 PUBLIC KEY ----
+Comment: "1024-bit DSA, converted by user@machine from OpenSSH"
+AAAAB3NzaC1kc3MAAACBAKU8/avmkFeGnSqwYG7dZnQlG+01QNaxu3F5v0NcL/SRUW7Idp
+Uq8t14siK0mA6yjphLhOf5t8gugTEVBllP86ANSbFigH7WN3v6ydJWqm60pNhNHN//50cn
+NtIsXbxeq3VtsI64pkH1OJqeZDHLmu73k4T0EKOzsylSfF/wtVBJAAAAFQChpubLHViwPB
++jSvUb8e4THS7PBQAAAIAJD1PMCiTCQa1xyD/NCWOajCufTOIzKAhm6l+nlBVPiKI+262X
+pYt127Ke4mPL8XJBizoTjSQN08uHMg/8L6W/cdO2aZ+mhkBnS1xAm83DAwqLrDraR1w/4Q
+RFxr5Vbyy8qnejrPjTJobBN1BGsv84wHkjmoCn6pFIfkGYeATlJgAAAIAHYPU1zMVBTDWr
+u7SNC4G2UyWGWYYLjLytBVHfQmBa51CmqrSs2kCfGLGA1ynfYENsxcJq9nsXrb4i17H5BH
+JFkH0g7BUDpeBeLr8gsK3WgfqWwtZsDkltObw9chUD/siK6q/dk/fSIB2Ho0inev7k68Z5
+ZkNI4XOwuEssAVhmwA==
+---- END SSH2 PUBLIC KEY ----

+ 5 - 0
tests/ssh/extra/ecdsa256-public.pem

@@ -0,0 +1,5 @@
+---- BEGIN SSH2 PUBLIC KEY ----
+Comment: "256-bit ECDSA, converted by miko@YUE from OpenSSH"
+AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBI4QIXX6Mg0NWD6OvW
+qb/L88sTs56hqs5sXkAh6vr9NleFczaijCUyn/9qLz/CfWg1pqj3KgOiFZ4ByT3xYfJIs=
+---- END SSH2 PUBLIC KEY ----

+ 6 - 0
tests/ssh/extra/rsa-public.pem

@@ -0,0 +1,6 @@
+---- BEGIN SSH2 PUBLIC KEY ----
+Comment: "1024-bit RSA, converted by miko@YUE from OpenSSH"
+AAAAB3NzaC1yc2EAAAADAQABAAAAgQC458k0igFgcvkrDjUKo0gIgPW0+7QEPck7/DXIpG
+AkHzHDTQLu7/IzxBDCL7iQhF2eQwaR/s9SXjs2DV/OT3SXM3QjJO4PS3nHkzfP7Zjqm9LG
+SicBgR8x5LPmxoNVA3qOInMP5xhhgbKYYusuBMAlpEgq/T9uCvLIuvRnGhBTDw==
+---- END SSH2 PUBLIC KEY ----

+ 4 - 0
tests/ssh/ssh-testkey-ed25519-pub

@@ -0,0 +1,4 @@
+---- BEGIN SSH2 PUBLIC KEY ----
+Comment: "256-bit ED25519, converted by root@b9ac43ae19a4 from OpenSSH"
+AAAAC3NzaC1lZDI1NTE5AAAAIKBYb96Jh+GNPVVRyb4gQSwk++R89VAbg+PU/nPyLBgb
+---- END SSH2 PUBLIC KEY ----