Browse Source

Add X.509 APIs

Signed-off-by: Steffen Jaeckel <[email protected]>
Steffen Jaeckel 6 months ago
parent
commit
57effe5af7

+ 2 - 0
src/headers/tomcrypt_macros.h

@@ -6,6 +6,8 @@
 #define LTC_TMPVAR_(n, l) LTC_TMPVAR__(n, l)
 #define LTC_TMPVAR(n) LTC_TMPVAR_(LTC_ ## n ## _, __LINE__)
 
+#define LTC_BIT(n) (1u << (n))
+
 /* ---- HELPER MACROS ---- */
 #ifdef ENDIAN_NEUTRAL
 

+ 244 - 0
src/headers/tomcrypt_pk.h

@@ -1058,4 +1058,248 @@ int der_decode_generalizedtime(const unsigned char *in, unsigned long *inlen,
 
 int der_length_generalizedtime(const ltc_generalizedtime *gtime, unsigned long *outlen);
 
+/* X.509 specific enums, structs and APIs */
+
+typedef enum ltc_x509_details {
+   /* The Serial is an integer, but we provide it as a hex string. */
+   LTC_X509_SERIAL = 0,
+   /* The most commonly used elements in an X.509 Name.
+    * These ones will be converted to a UTF-8 String and
+    * stored inside the `str` pointer of the `ltc_x509_string`.
+    */
+   /* CommonName */
+   LTC_X509_CN,
+   /* Country Code */
+   LTC_X509_C,
+   /* Locality */
+   LTC_X509_L,
+   /* State or Province */
+   LTC_X509_ST,
+   /* Organisation */
+   LTC_X509_O,
+   /* OrganisationalUnit */
+   LTC_X509_OU,
+   /* EmailAddress */
+   LTC_X509_EMAIL,
+
+   /* GeneralName subtypes
+    *    GeneralName ::= CHOICE {
+    *         otherName                       [0]     OtherName,
+    *         rfc822Name                      [1]     IA5String,
+    *         dNSName                         [2]     IA5String,
+    *         x400Address                     [3]     ORAddress,
+    *         directoryName                   [4]     Name,
+    *         ediPartyName                    [5]     EDIPartyName,
+    *         uniformResourceIdentifier       [6]     IA5String,
+    *         iPAddress                       [7]     OCTET STRING,
+    *         registeredID                    [8]     OBJECT IDENTIFIER }
+    */
+   LTC_X509_OTHER_NAME = 30,
+   LTC_X509_RFC822_NAME,
+   LTC_X509_DNS_NAME,
+   LTC_X509_X400_ADDRESS,
+   LTC_X509_DIRECTORY_NAME,
+   LTC_X509_EDI_PARTY_NAME,
+   LTC_X509_UNIFORM_RESOURCE_IDENTIFIER,
+   LTC_X509_IP_ADDRESS,
+   LTC_X509_REGISTERED_ID,
+
+   /* Value was encoded as an OCTET STRING */
+   LTC_X509_OCTET_STRING,
+
+   /* The most commonly used X.509 Certificate Extension. */
+   /* AuthorityKeyIdentifier */
+   LTC_X509_CE_AUTHORITY_KEY_ID = 100,
+   /* SubjectKeyIdentifier */
+   LTC_X509_CE_SUBJECT_KEY_ID,
+   /* KeyUsage */
+   LTC_X509_CE_KEY_USAGE,
+   /* SubjectAltName */
+   LTC_X509_CE_SUBJECT_ALT_NAME,
+   /* BasicConstraints */
+   LTC_X509_CE_BASIC_CONSTRAINTS,
+   /* ExtendedKeyUsage */
+   LTC_X509_CE_EXT_KEY_USAGE,
+
+   /* The rest will not be decoded and has to be treated
+    * manually through the `asn1` pointer of the struct.
+    */
+   LTC_X509_UNKNOWN = 0x7fff,
+} ltc_x509_details;
+
+typedef struct ltc_x509_string {
+   ltc_x509_details type;
+   const char *str;
+   const ltc_asn1_list *asn1;
+} ltc_x509_string;
+
+typedef struct ltc_x509_name {
+   const ltc_x509_string *names;
+   unsigned long names_num;
+   const ltc_asn1_list *asn1;
+} ltc_x509_name;
+
+typedef struct ltc_x509_time {
+   union {
+      ltc_utctime *utc;
+      ltc_generalizedtime *generalized;
+   } u;
+   int utc;
+   const char *str;
+   const ltc_asn1_list *asn1;
+} ltc_x509_time;
+
+typedef struct ltc_x509_signature_algorithm {
+   enum ltc_pka_id pka;
+   union {
+      const char *hash;
+      ltc_rsa_parameters rsa_params;
+   } u;
+   const ltc_asn1_list *asn1;
+} ltc_x509_signature_algorithm;
+
+typedef struct ltc_x509_validity {
+   ltc_x509_time not_before, not_after;
+} ltc_x509_validity;
+
+/*
+ * Certificate Extensions
+ */
+
+/* KeyUsage ::= BIT STRING */
+typedef enum ltc_x509_ce_key_usage {
+   /* digitalSignature        (0) */
+   LTC_KU_DS   = LTC_BIT(0),
+   /* contentCommitment       (1) */
+   LTC_KU_CC   = LTC_BIT(1),
+   /* keyEncipherment         (2) */
+   LTC_KU_KE   = LTC_BIT(2),
+   /* dataEncipherment        (3) */
+   LTC_KU_DE   = LTC_BIT(3),
+   /* keyAgreement            (4) */
+   LTC_KU_KA   = LTC_BIT(4),
+   /* keyCertSign             (5) */
+   LTC_KU_KCS  = LTC_BIT(5),
+   /* cRLSign                 (6) */
+   LTC_KU_CRLS = LTC_BIT(6),
+   /* encipherOnly            (7) */
+   LTC_KU_EO   = LTC_BIT(7),
+   /* decipherOnly            (8) */
+   LTC_KU_DO   = LTC_BIT(8),
+} ltc_x509_ce_key_usage;
+
+/* ExtKeyUsageSyntax ::= SEQUENCE SIZE (1..MAX) OF KeyPurposeId
+ * KeyPurposeId ::= OBJECT IDENTIFIER
+ */
+typedef enum ltc_x509_ce_ext_key_usage {
+   /* anyExtendedKeyUsage */
+   LTC_EKU_ANY  = LTC_BIT(0),
+   /* serverAuth */
+   LTC_EKU_SA   = LTC_BIT(1),
+   /* clientAuth */
+   LTC_EKU_CA   = LTC_BIT(2),
+   /* codeSigning */
+   LTC_EKU_CS   = LTC_BIT(3),
+   /* emailProtection */
+   LTC_EKU_EP   = LTC_BIT(4),
+   /* timeStamping */
+   LTC_EKU_TS   = LTC_BIT(5),
+   /* OCSPSigning */
+   LTC_EKU_OS   = LTC_BIT(6),
+} ltc_x509_ce_ext_key_usage;
+
+typedef struct ltc_x509_extension {
+   ltc_x509_details type;
+   const ltc_asn1_list *oid;
+   int critical;
+   union {
+      /* .type = LTC_X509_AUTHORITY_KEY_ID */
+      struct {
+         ltc_x509_string key_identifier;
+         ltc_x509_string authority_cert_issuer;
+         ltc_x509_string authority_cert_serial_number;
+      } authority_key_id;
+      /* .type = LTC_X509_SUBJECT_KEY_ID */
+      ltc_x509_string subject_key_identifier;
+      /* .type = LTC_X509_KEY_USAGE
+       * Bitmask of `enum ltc_x509_ce_key_usage`
+       */
+      ulong32 key_usage;
+      /* .type = LTC_X509_SUBJECT_ALT_NAME */
+      ltc_x509_name subject_alt_name;
+      /* .type = LTC_X509_BASIC_CONSTRAINTS */
+      struct {
+         int ca;
+         /* pathLenConstraint is marked as OPTIONAL:
+          * -1 -> value missing
+          * 0..INT_MAX -> valid values
+          */
+         int path_len;
+      } basic_constraints;
+      /* .type = LTC_X509_EXT_KEY_USAGE
+       * Bitmask of `enum ltc_x509_ce_ext_key_usage`
+       */
+      ulong32 ext_key_usage;
+   } u;
+   const ltc_asn1_list *asn1;
+} ltc_x509_extension;
+
+typedef struct ltc_x509_extensions {
+   const ltc_x509_extension *extensions;
+   unsigned long extensions_num;
+   const ltc_asn1_list *asn1;
+
+   const ltc_x509_extension *authority_key_id;
+   const ltc_x509_extension *subject_key_identifier;
+   const ltc_x509_extension *key_usage;
+   const ltc_x509_extension *subject_alt_name;
+   const ltc_x509_extension *basic_constraints;
+   const ltc_x509_extension *ext_key_usage;
+   /* let's pre-reserve this for future extensions */
+   const ltc_x509_extension *more[7];
+} ltc_x509_extensions;
+
+typedef struct ltc_x509_tbs_certificate {
+   unsigned long version;
+   ltc_x509_string serial_number;
+   ltc_x509_signature_algorithm signature_algorithm;
+   ltc_x509_name issuer;
+   ltc_x509_validity validity;
+   ltc_x509_name subject;
+   ltc_pka_key subject_public_key_info;
+   const ltc_asn1_list *issuer_uid;
+   const ltc_asn1_list *subject_uid;
+   ltc_x509_extensions extensions;
+   const ltc_asn1_list *asn1;
+} ltc_x509_tbs_certificate;
+
+typedef struct ltc_x509_signature {
+   const unsigned char *signature;
+   /* The signature length is given in bits, but it is stored as bytes.
+    * I.e. a signature of 123 bits will be stored in 128bits resp. 16bytes.
+    */
+   unsigned long signature_len;
+   const ltc_asn1_list *asn1;
+} ltc_x509_signature;
+
+typedef struct ltc_x509_certificate {
+   ltc_x509_tbs_certificate tbs_certificate;
+   ltc_x509_signature_algorithm signature_algorithm;
+   ltc_x509_signature signature;
+   const ltc_asn1_list *asn1;
+} ltc_x509_certificate;
+
+int x509_import(const unsigned char *asn1_cert, unsigned long asn1_len, const ltc_x509_certificate **out);
+#ifdef LTC_PEM
+int x509_import_pem(const char *pem, unsigned long *pem_len, const ltc_x509_certificate **out);
+#ifndef LTC_NO_FILE
+int x509_import_pem_filehandle(FILE *f, const ltc_x509_certificate **out);
+#endif /* LTC_NO_FILE */
+#endif /* LTC_PEM */
+int x509_cert_is_signed_by(const ltc_x509_certificate *cert, const ltc_pka_key *key, int *stat);
+int x509_cmp_name(const ltc_x509_name *a, const ltc_x509_name *b);
+int x509_name_detail_get(const ltc_x509_name *name, ltc_x509_details type, const ltc_x509_string **str);
+const char *x509_name_detail_desc(ltc_x509_details type);
+void x509_free(const ltc_x509_certificate **cert);
+
 #endif

+ 43 - 6
src/headers/tomcrypt_private.h

@@ -63,6 +63,33 @@ enum ltc_oid_id {
    LTC_OID_RSA_OAEP,
    LTC_OID_RSA_MGF1,
    LTC_OID_RSA_PSS,
+   LTC_OID_RSA_WITH_MD5,
+   LTC_OID_RSA_WITH_SHA1,
+   LTC_OID_RSA_WITH_SHA224,
+   LTC_OID_RSA_WITH_SHA256,
+   LTC_OID_RSA_WITH_SHA384,
+   LTC_OID_RSA_WITH_SHA512,
+   LTC_OID_RSA_WITH_SHA512_224,
+   LTC_OID_RSA_WITH_SHA512_512,
+   LTC_OID_ECDSA_WITH_SHA1,
+   LTC_OID_ECDSA_WITH_SHA224,
+   LTC_OID_ECDSA_WITH_SHA256,
+   LTC_OID_ECDSA_WITH_SHA384,
+   LTC_OID_ECDSA_WITH_SHA512,
+   LTC_OID_DSA_WITH_SHA1,
+   LTC_OID_DSA_WITH_SHA224,
+   LTC_OID_DSA_WITH_SHA256,
+   LTC_OID_DSA_WITH_SHA384,
+   LTC_OID_DSA_WITH_SHA512,
+   LTC_OID_ECDSA_WITH_SHA3_224,
+   LTC_OID_ECDSA_WITH_SHA3_256,
+   LTC_OID_ECDSA_WITH_SHA3_384,
+   LTC_OID_ECDSA_WITH_SHA3_512,
+   LTC_OID_RSA_WITH_SHA3_224,
+   LTC_OID_RSA_WITH_SHA3_256,
+   LTC_OID_RSA_WITH_SHA3_384,
+   LTC_OID_RSA_WITH_SHA3_512,
+
    LTC_OID_NUM
 };
 
@@ -424,13 +451,14 @@ int rand_bn_upto(void *N, void *limit, prng_state *prng, int wprng);
 
 int pk_get_oid(enum ltc_oid_id id, const char **st);
 int pk_get_pka_id(enum ltc_oid_id id, enum ltc_pka_id *pka);
+int pk_get_sig_alg(enum ltc_oid_id id, ltc_x509_signature_algorithm *sig_alg);
 int pk_get_oid_id(enum ltc_pka_id pka, enum ltc_oid_id *oid);
+int pk_oid_str_to_num(const char *OID, unsigned long *oid, unsigned long *oidlen);
+int pk_oid_num_to_str(const unsigned long *oid, unsigned long oidlen, char *OID, unsigned long *outlen);
 #ifdef LTC_DER
 int pk_get_oid_from_asn1(const ltc_asn1_list *oid, enum ltc_oid_id *id);
+int pk_oid_cmp_with_asn1(const char *o1, const ltc_asn1_list *o2);
 #endif
-int pk_oid_str_to_num(const char *OID, unsigned long *oid, unsigned long *oidlen);
-int pk_oid_num_to_str(const unsigned long *oid, unsigned long oidlen, char *OID, unsigned long *outlen);
-
 int pk_oid_cmp_with_ulong(const char *o1, const unsigned long *o2, unsigned long o2size);
 
 /* ---- DH Routines ---- */
@@ -695,6 +723,7 @@ int ec25519_crypto_ctx(      unsigned char *out, unsigned long *outlen,
 #define LTC_ASN1_IS_TYPE(e, t) (((e) != NULL) && ((e)->type == (t)))
 
 /* DER handling */
+int der_decode_sequence_flexi_limited(const unsigned char *in, unsigned long *inlen, long max_depth, ltc_asn1_list **out);
 int der_decode_custom_type_ex(const unsigned char *in, unsigned long  inlen,
                            ltc_asn1_list *root,
                            ltc_asn1_list *list,     unsigned long  outlen, unsigned int flags);
@@ -713,6 +742,12 @@ int der_length_sequence_ex(const ltc_asn1_list *list, unsigned long inlen,
 int der_length_object_identifier_full(const unsigned long *words,  unsigned long  nwords,
                                             unsigned long *outlen, unsigned long *datalen);
 
+int der_decode_ia5_string_data(const unsigned char *in, unsigned long inlen,
+                                              char *out, unsigned long *outlen);
+int der_decode_object_identifier_data(const unsigned char *in,    unsigned long  inlen,
+                                            unsigned long *words, unsigned long *outlen);
+
+
 int der_ia5_char_encode(int c);
 int der_ia5_value_decode(int v);
 
@@ -784,9 +819,11 @@ int x509_decode_subject_public_key_info(const unsigned char *in, unsigned long i
         ltc_asn1_type parameters_type, ltc_asn1_list* parameters, unsigned long *parameters_len);
 
 int x509_get_pka(const ltc_asn1_list *pub, enum ltc_pka_id *pka);
-int x509_import_spki(const unsigned char *asn1_cert, unsigned long asn1_len, ltc_pka_key *k, ltc_asn1_list **root);
-
-int pk_oid_cmp_with_asn1(const char *o1, const ltc_asn1_list *o2);
+int x509_get_sig_alg(const ltc_asn1_list *pub, ltc_x509_signature_algorithm *sig_alg);
+int x509_import_spki(const unsigned char *buf, unsigned long len, ltc_pka_key *k, ltc_asn1_list **root);
+int x509_get_extensions(const ltc_asn1_list *seq, ltc_x509_extensions *extensions);
+void x509_free_extensions(const ltc_x509_extensions *extensions);
+int x509_get_serial(const ltc_asn1_list *asn1, ltc_x509_string *serial);
 
 #endif /* LTC_DER */
 

+ 26 - 18
src/pk/asn1/der/ia5/der_decode_ia5_string.c

@@ -9,6 +9,30 @@
 
 
 #ifdef LTC_DER
+int der_decode_ia5_string_data(const unsigned char *in, unsigned long inlen,
+                                              char *out, unsigned long *outlen)
+{
+   unsigned long x, y;
+   int           t;
+
+   if (inlen > *outlen) {
+      *outlen = inlen;
+      return CRYPT_BUFFER_OVERFLOW;
+   }
+
+   /* read the data */
+   for (x = y = 0; y < inlen; y++) {
+       t = der_ia5_value_decode(in[x++]);
+       if (t == -1) {
+           return CRYPT_INVALID_ARG;
+       }
+       out[y] = t;
+   }
+
+   *outlen = y;
+
+   return CRYPT_OK;
+}
 
 /**
   Store a IA5 STRING
@@ -22,7 +46,7 @@ int der_decode_ia5_string(const unsigned char *in, unsigned long inlen,
                                 unsigned char *out, unsigned long *outlen)
 {
    unsigned long x, y, len;
-   int           t, err;
+   int           err;
 
    LTC_ARGCHK(in     != NULL);
    LTC_ARGCHK(out    != NULL);
@@ -47,27 +71,11 @@ int der_decode_ia5_string(const unsigned char *in, unsigned long inlen,
    x += y;
 
    /* is it too long? */
-   if (len > *outlen) {
-      *outlen = len;
-      return CRYPT_BUFFER_OVERFLOW;
-   }
-
    if (len > (inlen - x)) {
       return CRYPT_INVALID_PACKET;
    }
 
-   /* read the data */
-   for (y = 0; y < len; y++) {
-       t = der_ia5_value_decode(in[x++]);
-       if (t == -1) {
-           return CRYPT_INVALID_ARG;
-       }
-       out[y] = t;
-   }
-
-   *outlen = y;
-
-   return CRYPT_OK;
+   return der_decode_ia5_string_data(in + x, len, (char*)out, outlen);
 }
 
 #endif

+ 45 - 36
src/pk/asn1/der/object_identifier/der_decode_object_identifier.c

@@ -8,6 +8,49 @@
 */
 
 #ifdef LTC_DER
+int der_decode_object_identifier_data(const unsigned char *in,    unsigned long  inlen,
+                                            unsigned long *words, unsigned long *outlen)
+{
+   unsigned long x, y, t, len;
+   int err;
+
+   /* decode words */
+   x = y = t = 0;
+   len = inlen;
+   while (len--) {
+      t = (t << 7) | (in[x] & 0x7F);
+      if (!(in[x++] & 0x80)) {
+         /* store t */
+         if (y >= *outlen) {
+            y++;
+         } else {
+            if (y == 0) {
+               if (t <= 79) {
+                  words[0] = t / 40;
+                  words[1] = t % 40;
+               } else {
+                  words[0] = 2;
+                  words[1] = t - 80;
+               }
+               y = 2;
+            } else {
+               words[y++] = t;
+            }
+         }
+         t = 0;
+      }
+   }
+
+   if (y > *outlen) {
+      err =  CRYPT_BUFFER_OVERFLOW;
+   } else {
+      err =  CRYPT_OK;
+   }
+
+   *outlen = y;
+   return err;
+}
+
 /**
   Decode OID data and store the array of integers in words
   @param in      The OID DER encoded data
@@ -19,7 +62,7 @@
 int der_decode_object_identifier(const unsigned char *in,    unsigned long  inlen,
                                        unsigned long *words, unsigned long *outlen)
 {
-   unsigned long x, y, t, len;
+   unsigned long x, y, len;
    int err;
 
    LTC_ARGCHK(in     != NULL);
@@ -54,41 +97,7 @@ int der_decode_object_identifier(const unsigned char *in,    unsigned long  inle
       return CRYPT_INVALID_PACKET;
    }
 
-   /* decode words */
-   y = 0;
-   t = 0;
-   while (len--) {
-      t = (t << 7) | (in[x] & 0x7F);
-      if (!(in[x++] & 0x80)) {
-         /* store t */
-         if (y >= *outlen) {
-            y++;
-         } else {
-            if (y == 0) {
-               if (t <= 79) {
-                  words[0] = t / 40;
-                  words[1] = t % 40;
-               } else {
-                  words[0] = 2;
-                  words[1] = t - 80;
-               }
-               y = 2;
-            } else {
-               words[y++] = t;
-            }
-         }
-         t = 0;
-      }
-   }
-
-   if (y > *outlen) {
-      err =  CRYPT_BUFFER_OVERFLOW;
-   } else {
-      err =  CRYPT_OK;
-   }
-
-   *outlen = y;
-   return err;
+   return der_decode_object_identifier_data(in + x, len, words, outlen);
 }
 
 #endif

+ 19 - 3
src/pk/asn1/der/sequence/der_decode_sequence_flexi.c

@@ -52,7 +52,7 @@ void s_print_err(const char *errstr, ltc_asn1_list *l, int err, unsigned long id
    @param depth   The depth/level of decoding recursion we've already reached
    @return CRYPT_OK on success.
 */
-static int s_der_decode_sequence_flexi(const unsigned char *in, unsigned long *inlen, ltc_asn1_list **out, unsigned long depth)
+static int s_der_decode_sequence_flexi(const unsigned char *in, unsigned long *inlen, ltc_asn1_list **out, unsigned long depth, long max_depth)
 {
    ltc_asn1_list *l;
    int err;
@@ -433,6 +433,9 @@ static int s_der_decode_sequence_flexi(const unsigned char *in, unsigned long *i
              if (depth > LTC_DER_MAX_RECURSION) {
                 err = CRYPT_PK_ASN1_ERROR;
                 goto error;
+             } else if (max_depth > 0 && (long)depth > max_depth) {
+                totlen = *inlen;
+                goto default_out;
              }
 
              if ((l->data = XMALLOC(len)) == NULL) {
@@ -453,7 +456,7 @@ static int s_der_decode_sequence_flexi(const unsigned char *in, unsigned long *i
              len_len = len;
 
              /* Sequence elements go as child */
-             if ((err = s_der_decode_sequence_flexi(in, &len, &(l->child), depth+1)) != CRYPT_OK) {
+             if ((err = s_der_decode_sequence_flexi(in, &len, &(l->child), depth+1, max_depth)) != CRYPT_OK) {
                 goto error;
              }
              if (len_len != len) {
@@ -489,6 +492,7 @@ static int s_der_decode_sequence_flexi(const unsigned char *in, unsigned long *i
              break;
 
          default:
+default_out:
            /* invalid byte ... this is a soft error */
            /* remove link */
            if (l->prev) {
@@ -540,7 +544,19 @@ error:
 */
 int der_decode_sequence_flexi(const unsigned char *in, unsigned long *inlen, ltc_asn1_list **out)
 {
-   return s_der_decode_sequence_flexi(in, inlen, out, 0);
+   return s_der_decode_sequence_flexi(in, inlen, out, 0, -1);
+}
+
+/**
+   ASN.1 DER Flexi(ble) decoder will decode arbitrary DER packets and create a linked list of the decoded elements.
+   @param in      The input buffer
+   @param inlen   [in/out] The length of the input buffer and on output the amount of decoded data
+   @param out     [out] A pointer to the linked list
+   @return CRYPT_OK on success.
+*/
+int der_decode_sequence_flexi_limited(const unsigned char *in, unsigned long *inlen, long max_depth, ltc_asn1_list **out)
+{
+   return s_der_decode_sequence_flexi(in, inlen, out, 0, max_depth);
 }
 
 #endif

+ 36 - 0
src/pk/asn1/oid/pk_get.c

@@ -23,6 +23,32 @@ static const oid_table_entry pka_oids[] = {
                                               { LTC_OID_RSA_OAEP,             LTC_PKA_RSA,     NULL,         "1.2.840.113549.1.1.7" },
                                               { LTC_OID_RSA_MGF1,             LTC_PKA_RSA,     NULL,         "1.2.840.113549.1.1.8" },
                                               { LTC_OID_RSA_PSS,              LTC_PKA_RSA_PSS, NULL,         "1.2.840.113549.1.1.10" },
+                                              { LTC_OID_RSA_WITH_MD5,         LTC_PKA_RSA,     "md5",        "1.2.840.113549.1.1.4" },
+                                              { LTC_OID_RSA_WITH_SHA1,        LTC_PKA_RSA,     "sha1",       "1.2.840.113549.1.1.5" },
+                                              { LTC_OID_RSA_WITH_SHA224,      LTC_PKA_RSA,     "sha224",     "1.2.840.113549.1.1.14" },
+                                              { LTC_OID_RSA_WITH_SHA256,      LTC_PKA_RSA,     "sha256",     "1.2.840.113549.1.1.11" },
+                                              { LTC_OID_RSA_WITH_SHA384,      LTC_PKA_RSA,     "sha384",     "1.2.840.113549.1.1.12" },
+                                              { LTC_OID_RSA_WITH_SHA512,      LTC_PKA_RSA,     "sha512",     "1.2.840.113549.1.1.13" },
+                                              { LTC_OID_RSA_WITH_SHA512_224,  LTC_PKA_RSA,     "sha512-224", "1.2.840.113549.1.1.15" },
+                                              { LTC_OID_RSA_WITH_SHA512_512,  LTC_PKA_RSA,     "sha512-256", "1.2.840.113549.1.1.16" },
+                                              { LTC_OID_ECDSA_WITH_SHA1,      LTC_PKA_EC,      "sha1",       "1.2.840.10045.4.1" },
+                                              { LTC_OID_ECDSA_WITH_SHA224,    LTC_PKA_EC,      "sha224",     "1.2.840.10045.4.3.1" },
+                                              { LTC_OID_ECDSA_WITH_SHA256,    LTC_PKA_EC,      "sha256",     "1.2.840.10045.4.3.2" },
+                                              { LTC_OID_ECDSA_WITH_SHA384,    LTC_PKA_EC,      "sha384",     "1.2.840.10045.4.3.3" },
+                                              { LTC_OID_ECDSA_WITH_SHA512,    LTC_PKA_EC,      "sha512",     "1.2.840.10045.4.3.4" },
+                                              { LTC_OID_DSA_WITH_SHA1,        LTC_PKA_DSA,     "sha1",       "1.2.840.10040.4.3" },
+                                              { LTC_OID_DSA_WITH_SHA224,      LTC_PKA_DSA,     "sha224",     "2.16.840.1.101.3.4.3.1" },
+                                              { LTC_OID_DSA_WITH_SHA256,      LTC_PKA_DSA,     "sha256",     "2.16.840.1.101.3.4.3.2" },
+                                              { LTC_OID_DSA_WITH_SHA384,      LTC_PKA_DSA,     "sha384",     "2.16.840.1.101.3.4.3.3" },
+                                              { LTC_OID_DSA_WITH_SHA512,      LTC_PKA_DSA,     "sha512",     "2.16.840.1.101.3.4.3.4" },
+                                              { LTC_OID_ECDSA_WITH_SHA3_224,  LTC_PKA_EC,      "sha3-224",   "2.16.840.1.101.3.4.3.9" },
+                                              { LTC_OID_ECDSA_WITH_SHA3_256,  LTC_PKA_EC,      "sha3-256",   "2.16.840.1.101.3.4.3.10" },
+                                              { LTC_OID_ECDSA_WITH_SHA3_384,  LTC_PKA_EC,      "sha3-384",   "2.16.840.1.101.3.4.3.11" },
+                                              { LTC_OID_ECDSA_WITH_SHA3_512,  LTC_PKA_EC,      "sha3-512",   "2.16.840.1.101.3.4.3.12" },
+                                              { LTC_OID_RSA_WITH_SHA3_224,    LTC_PKA_RSA,     "sha3-512",   "2.16.840.1.101.3.4.3.13" },
+                                              { LTC_OID_RSA_WITH_SHA3_256,    LTC_PKA_RSA,     "sha3-512",   "2.16.840.1.101.3.4.3.14" },
+                                              { LTC_OID_RSA_WITH_SHA3_384,    LTC_PKA_RSA,     "sha3-512",   "2.16.840.1.101.3.4.3.15" },
+                                              { LTC_OID_RSA_WITH_SHA3_512,    LTC_PKA_RSA,     "sha3-512",   "2.16.840.1.101.3.4.3.16" },
 };
 
 static LTC_INLINE const oid_table_entry* s_get_entry(enum ltc_oid_id id)
@@ -76,6 +102,16 @@ int pk_get_pka_id(enum ltc_oid_id id, enum ltc_pka_id *pka)
    return s_get_values(id, pka, NULL);
 }
 
+/*
+   Returns the Signature Algorithm requested, PKA ID + Hash algorithm.
+   @return CRYPT_OK if valid
+*/
+int pk_get_sig_alg(enum ltc_oid_id id, ltc_x509_signature_algorithm *sig_alg)
+{
+   LTC_ARGCHK(sig_alg != NULL);
+   return s_get_values(id, &sig_alg->pka, &sig_alg->u.hash);
+}
+
 /*
    Returns the OID ID requested.
    @return CRYPT_OK if valid

+ 656 - 0
src/pk/asn1/x509/x509_extensions.c

@@ -0,0 +1,656 @@
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis */
+/* SPDX-License-Identifier: Unlicense */
+#include "tomcrypt_private.h"
+
+/**
+  @file x509_extensions.c
+  Extensions part of an X.509 cert, Steffen Jaeckel
+*/
+
+#ifdef LTC_DER
+
+#define OID_DETAIL_ELEMENT_VA(d, n, ...) { .detail = d, .node = # n, __VA_ARGS__ }
+
+#define st_oid_detail st_x509_extension_oid_detail
+
+typedef struct st_oid_detail {
+   ltc_x509_details detail;
+   const char *node;
+   union {
+      struct {
+         ltc_asn1_type type;
+         der_flexi_handler handler;
+      } ce;
+      struct {
+         ulong32 bit;
+      } eku;
+   } u;
+   ltc_x509_string *str;
+} st_oid_detail;
+
+static LTC_INLINE int s_get_element_(const st_oid_detail* details, unsigned long num, const char *arc, unsigned long arclen, const ltc_asn1_list *oid, const st_oid_detail **result)
+{
+   char oid_str[LTC_OID_MAX_STRLEN] = { 0 };
+   unsigned long i, oid_str_len = sizeof(oid_str);
+   const char *node;
+   int err;
+   *result = NULL;
+   if (oid->type != LTC_ASN1_OBJECT_IDENTIFIER) {
+      return CRYPT_INVALID_PACKET;
+   }
+   if ((err = pk_oid_num_to_str(oid->data, oid->size, oid_str, &oid_str_len)) != CRYPT_OK) {
+      return err;
+   }
+   if (XMEMCMP(oid_str, arc, arclen)) {
+      return CRYPT_OK;
+   }
+   node = oid_str + arclen;
+   if (*node != '.') {
+      return CRYPT_INVALID_PACKET;
+   }
+   node++;
+   for (i = 0; i < num; ++i) {
+      if (XSTRCMP(node, details[i].node) == 0) {
+         *result = &details[i];
+         break;
+      }
+   }
+   return CRYPT_OK;
+}
+#define s_get_element(arr, arc, oid, res) s_get_element_(arr, LTC_ARRAY_SIZE(arr), arc, sizeof(arc) - 1, oid, res)
+
+
+#ifndef S_FREE
+#define S_FREE
+#define s_free(p) s_free_((void*) p)
+static LTC_INLINE void s_free_(void* p)
+{
+   if (p == NULL) {
+      return;
+   }
+   XFREE(p);
+}
+#endif
+
+#ifndef S_FREE_X509_STRING_ARRAY
+#define S_FREE_X509_STRING_ARRAY
+#define s_free_x509_string_array(s, n) s_free_x509_string_array_((ltc_x509_string*)s, n)
+static LTC_INLINE void s_free_x509_string_array_(ltc_x509_string *strings, unsigned long num)
+{
+   unsigned long n;
+   for (n = num; n --> 0;) {
+      s_free(strings[n].str);
+   }
+   s_free(strings);
+}
+#endif
+
+#ifndef S_IS_CONTEXT_SPECIFIC
+#define S_IS_CONTEXT_SPECIFIC
+static LTC_INLINE int s_is_context_specific(const ltc_asn1_list *seq)
+{
+   if (seq->type != LTC_ASN1_CUSTOM_TYPE)
+      return 0;
+   if (seq->klass != LTC_ASN1_CL_CONTEXT_SPECIFIC)
+      return 0;
+   return 1;
+}
+#endif
+
+#ifndef S_IS_CONTEXT_SPECIFIC_PRIMITIVE
+#define S_IS_CONTEXT_SPECIFIC_PRIMITIVE
+static LTC_INLINE int s_is_context_specific_primitive(const ltc_asn1_list *seq)
+{
+   if (!s_is_context_specific(seq))
+      return 0;
+   if (seq->pc != LTC_ASN1_PC_PRIMITIVE)
+      return 0;
+   return 1;
+}
+#endif
+
+static LTC_INLINE int s_looks_like_general_name(const ltc_asn1_list *name)
+{
+   if (!s_is_context_specific(name)
+         || (name->pc == LTC_ASN1_PC_PRIMITIVE && name->tag > 8))
+      return CRYPT_PK_ASN1_ERROR;
+   return CRYPT_OK;
+}
+
+typedef unsigned short int ushort16;
+#if defined(ENDIAN_LITTLE)
+#define LTC_NTOHS(y)  ( ((ushort16)((y)[0] & 255)<<8) | ((ushort16)((y)[1] & 255)) )
+#else
+#define LTC_NTOHS(y)  (*((ushort16*)(y)))
+#endif
+
+/* RFC 5280, Ch. 4.2.1.6.  Subject Alternative Name
+ *    [...]
+ *    GeneralName ::= CHOICE {
+ *         otherName                       [0]     OtherName,
+ *         rfc822Name                      [1]     IA5String,
+ *         dNSName                         [2]     IA5String,
+ *         x400Address                     [3]     ORAddress,
+ *         directoryName                   [4]     Name,
+ *         ediPartyName                    [5]     EDIPartyName,
+ *         uniformResourceIdentifier       [6]     IA5String,
+ *         iPAddress                       [7]     OCTET STRING,
+ *         registeredID                    [8]     OBJECT IDENTIFIER }
+ *
+ *    OtherName ::= SEQUENCE {
+ *         type-id    OBJECT IDENTIFIER,
+ *         value      [0] EXPLICIT ANY DEFINED BY type-id }
+ *
+ *    EDIPartyName ::= SEQUENCE {
+ *         nameAssigner            [0]     DirectoryString OPTIONAL,
+ *         partyName               [1]     DirectoryString }
+ */
+static int s_get_general_name(const ltc_asn1_list *seq, ltc_x509_string *name)
+{
+   int err = CRYPT_OK;
+   char *str = NULL;
+   unsigned long len;
+
+   switch (seq->tag) {
+      case 1:
+      case 2:
+      case 6:
+         len = seq->size + 1;
+         str = XMALLOC(len);
+         if (str == NULL) {
+            err = CRYPT_MEM;
+            break;
+         }
+         if ((err = der_decode_ia5_string_data(seq->data, seq->size, str, &len)) != CRYPT_OK) {
+            break;
+         }
+         str[len] = '\0';
+         break;
+      case 7:
+      {
+         int nbytes;
+         unsigned char *ip = seq->data;
+         if (seq->size == 4) {
+            str = XMALLOC(4 * 4);
+            if (str == NULL) {
+               err = CRYPT_MEM;
+               break;
+            }
+            nbytes = snprintf(str, 16, "%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]);
+            if (nbytes < 0) {
+               err = CRYPT_ERROR;
+            }
+         } else if (seq->size == 16) {
+            str = XMALLOC(8 * 5);
+            if (str == NULL) {
+               err = CRYPT_MEM;
+               break;
+            }
+            nbytes = snprintf(str, 40, "%x:%x:%x:%x:%x:%x:%x:%x", LTC_NTOHS(&ip[0]),  LTC_NTOHS(&ip[2]),
+                                                                  LTC_NTOHS(&ip[4]),  LTC_NTOHS(&ip[6]),
+                                                                  LTC_NTOHS(&ip[8]),  LTC_NTOHS(&ip[10]),
+                                                                  LTC_NTOHS(&ip[12]), LTC_NTOHS(&ip[14]));
+            if (nbytes < 0) {
+               err = CRYPT_ERROR;
+            }
+         } else {
+            err = CRYPT_PK_ASN1_ERROR;
+         }
+      }
+         break;
+      case 8:
+      {
+         unsigned long oid[LTC_DER_OID_DEFAULT_NODES], oid_len = LTC_DER_OID_DEFAULT_NODES;
+         if ((err = der_decode_object_identifier_data(seq->data, seq->size, oid, &oid_len)) != CRYPT_OK) {
+            break;
+         }
+         if ((err = pk_oid_num_to_str(oid, oid_len, NULL, &len)) != CRYPT_BUFFER_OVERFLOW) {
+            break;
+         }
+         str = XMALLOC(len);
+         if (str == NULL) {
+            err = CRYPT_MEM;
+            break;
+         }
+         err = pk_oid_num_to_str(oid, oid_len, str, &len);
+      }
+         break;
+      case 0:
+      case 3:
+      case 4:
+      case 5:
+         break;
+      default:
+         err = CRYPT_PK_ASN1_ERROR;
+         break;
+   }
+   if (err != CRYPT_OK) {
+      if (str != NULL) {
+         XFREE(str);
+         str = NULL;
+      }
+      return err;
+   }
+   name->type = LTC_X509_OTHER_NAME + seq->tag;
+   name->asn1 = seq;
+   name->str = str;
+
+   return err;
+}
+
+static int s_octet_string_to_hex_string(const ltc_asn1_list *asn1, ltc_x509_string *hex)
+{
+   unsigned long len;
+   char *str;
+   int err;
+   len = asn1->size * 2 + 1;
+   str = XMALLOC(len);
+   if (str == NULL) {
+      return CRYPT_MEM;
+   }
+   if ((err = base16_encode(asn1->data, asn1->size, str, &len, 1)) != CRYPT_OK) {
+      XFREE(str);
+      return err;
+   }
+   hex->asn1 = asn1;
+   hex->str = str;
+   hex->type = LTC_X509_OCTET_STRING;
+   return CRYPT_OK;
+}
+
+/* RFC 5280, Ch. 4.2.1.1.  Authority Key Identifier
+ *    AuthorityKeyIdentifier ::= SEQUENCE {
+ *       keyIdentifier             [0] KeyIdentifier           OPTIONAL,
+ *       authorityCertIssuer       [1] GeneralNames            OPTIONAL,
+ *       authorityCertSerialNumber [2] CertificateSerialNumber OPTIONAL  }
+ *
+ *    KeyIdentifier ::= OCTET STRING
+ */
+static int s_get_aki(const ltc_asn1_list *seq, ltc_x509_extension *san)
+{
+   int err;
+   ltc_asn1_list *element = seq->child;
+   while(element && s_is_context_specific_primitive(element)) {
+      switch (element->tag) {
+         case 0:
+            if ((err = s_octet_string_to_hex_string(element, &san->u.authority_key_id.key_identifier)) != CRYPT_OK) {
+               return err;
+            }
+            break;
+         case 1:
+            if ((err = s_looks_like_general_name(element->child)) != CRYPT_OK) {
+               return err;
+            }
+            if ((err = s_get_general_name(element->child, &san->u.authority_key_id.authority_cert_issuer)) != CRYPT_OK) {
+               return err;
+            }
+            break;
+         case 2:
+            if ((err = x509_get_serial(element->child, &san->u.authority_key_id.authority_cert_serial_number)) != CRYPT_OK) {
+               return err;
+            }
+            break;
+         default:
+            return CRYPT_PK_ASN1_ERROR;
+      }
+      element = element->next;
+   }
+   return CRYPT_OK;
+}
+
+/* RFC 5280, Ch. 4.2.1.2.  Subject Key Identifier
+ *    SubjectKeyIdentifier ::= KeyIdentifier
+ *
+ *    KeyIdentifier ::= OCTET STRING
+ */
+static int s_get_ski(const ltc_asn1_list *seq, ltc_x509_extension *san)
+{
+   void *buf;
+   unsigned long len, outlen;
+   int err;
+   if (seq->type != LTC_ASN1_OCTET_STRING) {
+      return CRYPT_PK_ASN1_ERROR;
+   }
+   /* `size` still contains the ASN.1 header and length, so we're safe length-wise */
+   len = seq->size * 2;
+   buf = XMALLOC(len);
+   if (buf == NULL) {
+      return CRYPT_MEM;
+   }
+   outlen = len;
+   if ((err = der_decode_octet_string(seq->data, seq->size, buf, &outlen)) != CRYPT_OK) {
+      XFREE(buf);
+      return err;
+   }
+   if ((err = base16_encode(buf, outlen, buf, &len, 1)) != CRYPT_OK) {
+      XFREE(buf);
+      return err;
+   }
+   san->u.subject_key_identifier.asn1 = seq;
+   san->u.subject_key_identifier.str = buf;
+   san->u.subject_key_identifier.type = LTC_X509_OCTET_STRING;
+   return err;
+}
+
+/* RFC 5280, Ch. 4.2.1.3.  Key Usage
+ *    KeyUsage ::= BIT STRING {
+ *         digitalSignature        (0),
+ *         [...]
+ *         decipherOnly            (8) }
+ */
+static int s_get_ku(const ltc_asn1_list *bitstr, ltc_x509_extension *eku)
+{
+   unsigned char ku[9];
+   unsigned long n, kulen = sizeof(ku);
+   int err;
+   eku->u.key_usage = 0;
+   if ((err = der_decode_bit_string(bitstr->data, bitstr->size, ku, &kulen)) != CRYPT_OK) {
+      return err;
+   }
+   for (n = 0; n < kulen; ++n) {
+      eku->u.key_usage |= ku[n] ? (1 << n) : 0;
+   }
+   return err;
+}
+
+/* RFC 5280, Ch. 4.2.1.9.  Basic Constraints
+ *    BasicConstraints ::= SEQUENCE {
+ *       cA                      BOOLEAN DEFAULT FALSE,
+ *       pathLenConstraint       INTEGER (0..MAX) OPTIONAL }
+ */
+static int s_get_bc(const ltc_asn1_list *seq, ltc_x509_extension *bc)
+{
+   ltc_asn1_list *element = seq->child;
+   bc->u.basic_constraints.ca = 0;
+   bc->u.basic_constraints.path_len = -1;
+   if (element == NULL || element->type == LTC_ASN1_EOL)
+      return CRYPT_OK;
+   if (element->type == LTC_ASN1_BOOLEAN) {
+      bc->u.basic_constraints.ca = *(int*)element->data ? 1 : 0;
+      element = element->next;
+   }
+   if (element == NULL)
+      return CRYPT_OK;
+   if (element->type == LTC_ASN1_INTEGER) {
+      if (ltc_mp_count_bits(element->data) > (int)((sizeof(bc->u.basic_constraints.path_len) * CHAR_BIT) - 1))
+         return CRYPT_OVERFLOW;
+      bc->u.basic_constraints.path_len = (int)ltc_mp_get_int(element->data);
+   }
+   return CRYPT_OK;
+}
+
+/* RFC 5280, Ch. 4.2.1.12.  Extended Key Usage
+ *    id-ce-extKeyUsage OBJECT IDENTIFIER ::= { id-ce 37 }
+ *    ExtKeyUsageSyntax ::= SEQUENCE SIZE (1..MAX) OF KeyPurposeId
+ *    KeyPurposeId ::= OBJECT IDENTIFIER
+ */
+
+#define X509_EKU_ELEMENT(detail, node, type) OID_DETAIL_ELEMENT_VA(detail, node, .u.eku.bit = type)
+
+/* anyExtendedKeyUsage is allocated under the id-ce-extKeyUsage arc
+ *
+ * anyExtendedKeyUsage OBJECT IDENTIFIER ::= { id-ce-extKeyUsage 0 }
+ */
+static const char x509_eku_arc[] = "2.5.29.37";
+static const st_oid_detail eku_any_map[] = {
+                       X509_EKU_ELEMENT(LTC_X509_CE_EXT_KEY_USAGE, 0, LTC_EKU_ANY),
+};
+
+/* all other key purpose OID's are allocated under the id-pkix arc
+ *
+ * id-kp OBJECT IDENTIFIER ::= { id-pkix 3 }
+ */
+static const char x509_kp_arc[] = "1.3.6.1.5.5.7.3";
+static const st_oid_detail eku_elements_map[] = {
+                       X509_EKU_ELEMENT(LTC_X509_CE_EXT_KEY_USAGE, 1, LTC_EKU_SA),
+                       X509_EKU_ELEMENT(LTC_X509_CE_EXT_KEY_USAGE, 2, LTC_EKU_CA),
+                       X509_EKU_ELEMENT(LTC_X509_CE_EXT_KEY_USAGE, 3, LTC_EKU_CS),
+                       X509_EKU_ELEMENT(LTC_X509_CE_EXT_KEY_USAGE, 4, LTC_EKU_EP),
+                       X509_EKU_ELEMENT(LTC_X509_CE_EXT_KEY_USAGE, 8, LTC_EKU_TS),
+                       X509_EKU_ELEMENT(LTC_X509_CE_EXT_KEY_USAGE, 9, LTC_EKU_OS),
+};
+
+static int s_get_eku(const ltc_asn1_list *seq, ltc_x509_extension *eku)
+{
+   int err = CRYPT_INVALID_PACKET;
+   ltc_asn1_list *oid = seq->child;
+   eku->u.ext_key_usage = 0;
+   while (oid) {
+      const st_oid_detail *ce = NULL;
+      err = s_get_element(eku_any_map, x509_eku_arc, oid, &ce);
+      if (err == CRYPT_OK && ce) {
+         eku->u.ext_key_usage |= ce->u.eku.bit;
+         oid = oid->next;
+         continue;
+      } else if (err != CRYPT_OK) {
+         break;
+      }
+      err = s_get_element(eku_elements_map, x509_kp_arc, oid, &ce);
+      if (err == CRYPT_OK && ce) {
+         eku->u.ext_key_usage |= ce->u.eku.bit;
+      } else if (err != CRYPT_OK) {
+         break;
+      }
+      oid = oid->next;
+   }
+   if (eku->u.ext_key_usage == 0) {
+      err = CRYPT_INVALID_PACKET;
+   }
+   return err;
+}
+
+/* RFC 5280, Ch. 4.2.1.6.  Subject Alternative Name
+ *    SubjectAltName ::= GeneralNames
+ *
+ *    GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
+ */
+static int s_get_san(const ltc_asn1_list *seq, ltc_x509_extension *san)
+{
+   int err = CRYPT_PK_ASN1_ERROR;
+   ltc_x509_string *names;
+   unsigned long num = 0, cur = 0;
+   ltc_asn1_list *name = seq->child;
+
+   while (name) {
+      if ((err = s_looks_like_general_name(name)) != CRYPT_OK) {
+         return err;
+      }
+      num++;
+      name = name->next;
+   }
+
+   names = XCALLOC(num, sizeof(*names));
+   if (names == NULL) {
+      return CRYPT_MEM;
+   }
+   name = seq->child;
+
+   while (name && cur < num) {
+      if ((err = s_get_general_name(name, &names[cur])) != CRYPT_OK) {
+         break;
+      }
+      name = name->next;
+      cur++;
+   }
+   if (err == CRYPT_OK) {
+      san->u.subject_alt_name.asn1 = seq;
+      san->u.subject_alt_name.names = names;
+      san->u.subject_alt_name.names_num = num;
+   } else {
+      s_free_x509_string_array(names, num);
+   }
+   return err;
+}
+
+#define X509_CE_ELEMENT(detail, oid, type_, hndl) OID_DETAIL_ELEMENT_VA(detail, oid, .u.ce.type = type_, .u.ce.handler = (der_flexi_handler)hndl)
+
+/* The certificate extension OID's arc is defined as follows
+ *
+ * id-ce   OBJECT IDENTIFIER ::=  { joint-iso-ccitt(2) ds(5) 29 }
+ */
+static const char x509_ce_arc[] = "2.5.29";
+static const st_oid_detail ce_elements_map[] = {
+                       X509_CE_ELEMENT(LTC_X509_CE_AUTHORITY_KEY_ID,  35, LTC_ASN1_SEQUENCE, s_get_aki),
+                       X509_CE_ELEMENT(LTC_X509_CE_SUBJECT_KEY_ID,    14, LTC_ASN1_OCTET_STRING, s_get_ski),
+                       X509_CE_ELEMENT(LTC_X509_CE_KEY_USAGE,         15, LTC_ASN1_BIT_STRING, s_get_ku),
+                       X509_CE_ELEMENT(LTC_X509_CE_SUBJECT_ALT_NAME,  17, LTC_ASN1_SEQUENCE, s_get_san),
+                       X509_CE_ELEMENT(LTC_X509_CE_BASIC_CONSTRAINTS, 19, LTC_ASN1_SEQUENCE, s_get_bc),
+                       X509_CE_ELEMENT(LTC_X509_CE_EXT_KEY_USAGE,     37, LTC_ASN1_SEQUENCE, s_get_eku),
+};
+
+typedef struct st_ce_value {
+   const st_oid_detail* ce;
+   ltc_asn1_list *crit;
+   ltc_x509_extension value;
+} st_ce_value;
+
+static int s_get_ce_element(const ltc_asn1_list *oid, st_ce_value *ce)
+{
+   int err = s_get_element(ce_elements_map, x509_ce_arc, oid, &ce->ce);
+   ce->value.oid = (err == CRYPT_OK) ? oid : NULL;
+   return err;
+}
+
+static int s_get_ce_value(const ltc_asn1_list *os, st_ce_value *ce)
+{
+   int err = CRYPT_OK;
+   ce->value.asn1 = os;
+   if (ce->ce == NULL) {
+      ce->value.type = LTC_X509_UNKNOWN;
+      return CRYPT_OK;
+   }
+   if (ce->ce->u.ce.type == LTC_ASN1_SEQUENCE) {
+      ltc_asn1_list *value;
+      unsigned long len = os->size;
+      if ((err = der_decode_sequence_flexi_limited(os->data, &len, 2, &value)) != CRYPT_OK) {
+         return err;
+      }
+      if (value->type != LTC_ASN1_SEQUENCE) {
+         err = CRYPT_INVALID_PACKET;
+         goto err_out;
+      } else {
+         err = ce->ce->u.ce.handler(value, &ce->value);
+      }
+      der_free_sequence_flexi(value);
+   } else {
+      err = ce->ce->u.ce.handler(os, &ce->value);
+   }
+   if (err == CRYPT_OK) {
+      ce->value.type = ce->ce->detail;
+   }
+err_out:
+   return err;
+}
+
+static LTC_INLINE void s_free_extension(const ltc_x509_extension *ext)
+{
+   switch (ext->type) {
+      case LTC_X509_CE_AUTHORITY_KEY_ID:
+         s_free(ext->u.authority_key_id.key_identifier.str);
+         s_free(ext->u.authority_key_id.authority_cert_issuer.str);
+         s_free(ext->u.authority_key_id.authority_cert_serial_number.str);
+         break;
+      case LTC_X509_CE_SUBJECT_KEY_ID:
+         s_free(ext->u.subject_key_identifier.str);
+         break;
+      case LTC_X509_CE_SUBJECT_ALT_NAME:
+         s_free_x509_string_array(ext->u.subject_alt_name.names, ext->u.subject_alt_name.names_num);
+         break;
+      case LTC_X509_CE_BASIC_CONSTRAINTS:
+      case LTC_X509_CE_EXT_KEY_USAGE:
+      case LTC_X509_CE_KEY_USAGE:
+      default:
+         break;
+   }
+}
+
+static LTC_INLINE void s_free_extensions(const ltc_x509_extension *extensions, unsigned long num)
+{
+   unsigned long n;
+   for (n = num; n --> 0;) {
+      s_free_extension(&extensions[n]);
+   }
+   s_free(extensions);
+}
+
+void x509_free_extensions(const ltc_x509_extensions *extensions)
+{
+   s_free_extensions(extensions->extensions, extensions->extensions_num);
+}
+
+int x509_get_extensions(const ltc_asn1_list *seq, ltc_x509_extensions *extensions)
+{
+   ltc_x509_extension *extensions_;
+   unsigned long extensions_num = 0;
+   ltc_asn1_list *cur;
+   unsigned long cur_num = 0;
+   int err;
+   if (seq->type != LTC_ASN1_SEQUENCE)
+      return CRYPT_INVALID_PACKET;
+   cur = seq->child;
+   while (cur) {
+      extensions_num++;
+      cur = cur->next;
+   }
+   extensions_ = XCALLOC(extensions_num, sizeof(*extensions->extensions));
+   if (extensions_ == NULL) {
+      return CRYPT_MEM;
+   }
+   cur = seq->child;
+   while (cur) {
+      st_ce_value val = {0};
+      der_flexi_check flexi_should[4];
+      if (cur_num > extensions_num) {
+         err = CRYPT_ERROR;
+         goto error_out;
+      }
+      LTC_SET_DER_FLEXI_HANDLER(flexi_should, 0, LTC_ASN1_OBJECT_IDENTIFIER, s_get_ce_element, &val);
+      LTC_SET_DER_FLEXI_CHECK_OPT(flexi_should, 1, LTC_ASN1_BOOLEAN, &val.crit);
+      LTC_SET_DER_FLEXI_HANDLER(flexi_should, 2, LTC_ASN1_OCTET_STRING, s_get_ce_value, &val);
+      LTC_SET_DER_FLEXI_CHECK(flexi_should, 3, LTC_ASN1_EOL, NULL);
+      if ((err = der_flexi_sequence_cmp(cur, flexi_should)) != CRYPT_OK) {
+         goto error_out;
+      }
+      extensions_[cur_num] = val.value;
+      if (val.crit) {
+         extensions_[cur_num].critical = *(int*)val.crit->data;
+      }
+      cur_num++;
+      cur = cur->next;
+   }
+   for (cur_num = 0; cur_num < extensions_num; ++cur_num) {
+      switch (extensions_[cur_num].type) {
+         case LTC_X509_CE_AUTHORITY_KEY_ID:
+            extensions->authority_key_id = &extensions_[cur_num];
+            break;
+         case LTC_X509_CE_SUBJECT_KEY_ID:
+            extensions->subject_key_identifier = &extensions_[cur_num];
+            break;
+         case LTC_X509_CE_KEY_USAGE:
+            extensions->key_usage = &extensions_[cur_num];
+            break;
+         case LTC_X509_CE_SUBJECT_ALT_NAME:
+            extensions->subject_alt_name = &extensions_[cur_num];
+            break;
+         case LTC_X509_CE_BASIC_CONSTRAINTS:
+            extensions->basic_constraints = &extensions_[cur_num];
+            break;
+         case LTC_X509_CE_EXT_KEY_USAGE:
+            extensions->ext_key_usage = &extensions_[cur_num];
+            break;
+         default:
+            break;
+      }
+   }
+   extensions->asn1 = seq;
+   extensions->extensions = extensions_;
+   extensions->extensions_num = cur_num;
+   return CRYPT_OK;
+error_out:
+   s_free_extensions(extensions_, extensions_num);
+   return err;
+}
+
+#undef st_oid_detail
+
+#endif

+ 21 - 5
src/pk/asn1/x509/x509_get_pka.c → src/pk/asn1/x509/x509_get.c

@@ -4,16 +4,15 @@
 
 /**
   @file x509_get_pka.c
-  Extract the PKA from an X.509 cert, Steffen Jaeckel
+  Extract details from an X.509 cert, Steffen Jaeckel
 */
 
 #ifdef LTC_DER
 
-int x509_get_pka(const ltc_asn1_list *pub, enum ltc_pka_id *pka)
+static LTC_INLINE int s_x509_get_oid(const ltc_asn1_list *pub, enum ltc_oid_id *oid_id)
 {
-   der_flexi_check flexi_should[4];
+   der_flexi_check flexi_should[3];
    ltc_asn1_list *seqid, *id = NULL;
-   enum ltc_oid_id oid_id;
    int err;
    unsigned long n = 0;
    LTC_SET_DER_FLEXI_CHECK(flexi_should, n++, LTC_ASN1_SEQUENCE, &seqid);
@@ -29,10 +28,27 @@ int x509_get_pka(const ltc_asn1_list *pub, enum ltc_pka_id *pka)
    if (err != CRYPT_OK && err != CRYPT_INPUT_TOO_LONG) {
       return err;
    }
-   if ((err = pk_get_oid_from_asn1(id, &oid_id)) != CRYPT_OK) {
+   return pk_get_oid_from_asn1(id, oid_id);
+}
+
+int x509_get_pka(const ltc_asn1_list *pub, enum ltc_pka_id *pka)
+{
+   int err;
+   enum ltc_oid_id oid_id;
+   if ((err = s_x509_get_oid(pub, &oid_id)) != CRYPT_OK) {
       return err;
    }
    return pk_get_pka_id(oid_id, pka);
 }
 
+int x509_get_sig_alg(const ltc_asn1_list *seq, ltc_x509_signature_algorithm *sig_alg)
+{
+   int err;
+   enum ltc_oid_id oid_id;
+   if ((err = s_x509_get_oid(seq, &oid_id)) != CRYPT_OK) {
+      return err;
+   }
+   return pk_get_sig_alg(oid_id, sig_alg);
+}
+
 #endif /* LTC_DER */

+ 683 - 0
src/pk/asn1/x509/x509_import.c

@@ -0,0 +1,683 @@
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis */
+/* SPDX-License-Identifier: Unlicense */
+#include "tomcrypt_private.h"
+#include <wchar.h>
+
+/**
+  @file x509_import.c
+  Import an X.509 cert, Steffen Jaeckel
+*/
+
+#ifdef LTC_DER
+
+#define LTC_ASN1_BMP_STRING 30
+
+#define ASN1_STRING LTC_BIT(LTC_ASN1_TELETEX_STRING) \
+   | LTC_BIT(LTC_ASN1_PRINTABLE_STRING) \
+   | LTC_BIT(LTC_ASN1_UTF8_STRING) \
+   | LTC_BIT(LTC_ASN1_CUSTOM_TYPE)
+#define ASN1_IA5_STRING LTC_BIT(LTC_ASN1_IA5_STRING)
+
+#define OID_DETAIL_ELEMENT(detail, oid, types, ub) { detail, oid, types, ub, NULL }
+#define OID_DETAIL_ELEMENT_5(detail, arc, oid, types, ub) { detail, arc "." # oid, types, ub, NULL }
+
+#define st_oid_detail st_x509_import_oid_detail
+
+typedef struct st_oid_detail {
+   ltc_x509_details detail;
+   const char *oid;
+   ulong32 types;
+   unsigned long upper_bound;
+   ltc_x509_string *str;
+} st_oid_detail;
+
+#define X509_NAME_ARC "2.5.4"
+#define X509_NAME_ELEMENT(detail, oid, ub) OID_DETAIL_ELEMENT_5(detail, X509_NAME_ARC, oid, ASN1_STRING, ub)
+
+static const st_oid_detail name_elements_map[] = {
+                       X509_NAME_ELEMENT(LTC_X509_CN, 3,  64),
+                       X509_NAME_ELEMENT(LTC_X509_C,  6,  2),
+                       X509_NAME_ELEMENT(LTC_X509_L,  7,  128),
+                       X509_NAME_ELEMENT(LTC_X509_ST, 8,  128),
+                       X509_NAME_ELEMENT(LTC_X509_O,  10, 64),
+                       X509_NAME_ELEMENT(LTC_X509_OU, 11, 64),
+                       OID_DETAIL_ELEMENT(LTC_X509_EMAIL, "1.2.840.113549.1.9.1", ASN1_IA5_STRING, 255),
+};
+
+static LTC_INLINE const st_oid_detail* s_get_name_element(const char *oid)
+{
+   unsigned long i;
+   for (i = 0; i < LTC_ARRAY_SIZE(name_elements_map); ++i) {
+      if (XSTRCMP(oid, name_elements_map[i].oid) == 0) {
+         return &name_elements_map[i];
+      }
+   }
+   return NULL;
+}
+
+static LTC_INLINE int s_get_as_string(const ltc_asn1_list *value, char **str)
+{
+   char *work = NULL;
+   size_t str_len;
+   switch (value->type) {
+      case LTC_ASN1_UTF8_STRING:
+         str_len = wcstombs(NULL, value->data, 0);
+         if (str_len == (size_t)-1) {
+            /* In case `wcstombs()` can't decode the string, we
+             * don't error out, but leave it up to the user.
+             */
+            break;
+         }
+         str_len++;
+         work = XMALLOC(str_len);
+         if (work == NULL)
+            return CRYPT_MEM;
+         str_len = wcstombs(work, value->data, str_len);
+         if (str_len == (size_t)-1) {
+            XFREE(work);
+            return CRYPT_ERROR;
+         }
+         break;
+      case LTC_ASN1_IA5_STRING:
+      case LTC_ASN1_PRINTABLE_STRING:
+      case LTC_ASN1_TELETEX_STRING:
+         work = XMALLOC(value->size + 1);
+         if (work == NULL)
+            return CRYPT_MEM;
+         XMEMCPY(work, value->data, value->size);
+         work[value->size] = '\0';
+         break;
+      case LTC_ASN1_CUSTOM_TYPE:
+         if (value->tag == LTC_ASN1_BMP_STRING) {
+            /* We don't decode BMP Strings, but the user
+             * is free to do so themselves.
+             */
+            break;
+         }
+         /* FALLTHROUGH */
+      default:
+         return CRYPT_INVALID_ARG;
+   }
+   *str = work;
+   return CRYPT_OK;
+}
+
+static LTC_INLINE int s_get_name_element_by_oid(const ltc_asn1_list *oid, st_oid_detail *name_element)
+{
+   char oid_str[LTC_OID_MAX_STRLEN] = { 0 }, *str = NULL;
+   unsigned long oid_str_len = sizeof(oid_str);
+   ltc_asn1_list *value;
+   const st_oid_detail *detail;
+   static const st_oid_detail unknown_detail = { .detail = LTC_X509_UNKNOWN };
+   int err;
+   if (oid == NULL
+         || oid->type != LTC_ASN1_OBJECT_IDENTIFIER
+         || oid->next == NULL) {
+      return CRYPT_PK_ASN1_ERROR;
+   }
+   value = oid->next;
+   if ((err = pk_oid_num_to_str(oid->data, oid->size, oid_str, &oid_str_len)) != CRYPT_OK) {
+      return err;
+   }
+   detail = s_get_name_element(oid_str);
+   if (detail) {
+      if (!(LTC_BIT(value->type) & detail->types))
+         return CRYPT_PK_ASN1_ERROR;
+      if (value->size > detail->upper_bound)
+         return CRYPT_PK_ASN1_ERROR;
+      if ((err = s_get_as_string(value, &str)) != CRYPT_OK) {
+         return err;
+      }
+   } else {
+      detail = &unknown_detail;
+   }
+   if (name_element) {
+      ltc_x509_string *orig_str = name_element->str;
+      orig_str->type = detail->detail;
+      orig_str->asn1 = value;
+      orig_str->str = str;
+      *name_element = *detail;
+      name_element->str = orig_str;
+   }
+   return CRYPT_OK;
+}
+
+static LTC_INLINE int s_x509_get_name_process(const ltc_asn1_list *set, st_oid_detail *name_elements, unsigned long name_elements_num)
+{
+   unsigned long num = 0;
+   for (; set != NULL; set = set->next) {
+      ltc_asn1_list *inner = set->child;
+      int err;
+      if (num > name_elements_num) {
+         return CRYPT_BUFFER_OVERFLOW;
+      }
+      if (set->type != LTC_ASN1_SET
+            || inner == NULL
+            || inner->type != LTC_ASN1_SEQUENCE) {
+         return CRYPT_PK_ASN1_ERROR;
+      }
+      if ((err = s_get_name_element_by_oid(inner->child, &name_elements[num])) != CRYPT_OK) {
+         return err;
+      }
+      num++;
+   }
+   return CRYPT_OK;
+}
+
+#ifndef S_FREE
+#define S_FREE
+#define s_free(p) s_free_((void*) p)
+static LTC_INLINE void s_free_(void* p)
+{
+   if (p == NULL) {
+      return;
+   }
+   XFREE((void*)p);
+}
+#endif
+
+#ifndef S_FREE_X509_STRING_ARRAY
+#define S_FREE_X509_STRING_ARRAY
+#define s_free_x509_string_array(s, n) s_free_x509_string_array_((ltc_x509_string*)s, n)
+static LTC_INLINE void s_free_x509_string_array_(ltc_x509_string *strings, unsigned long num)
+{
+   unsigned long n;
+   for (n = num; n --> 0;) {
+      s_free(strings[n].str);
+   }
+   s_free(strings);
+}
+#endif
+
+#define SETBIT(v, n)    (v=((unsigned char)(v) | (1U << (unsigned char)(n))))
+#define CLRBIT(v, n)    (v=((unsigned char)(v) & ~(1U << (unsigned char)(n))))
+
+static LTC_INLINE int s_bit_string_to_raw_bit_string(const ltc_asn1_list *bs, unsigned char **out, unsigned long *outlen)
+{
+   unsigned char *r = bs->data, *w = XCALLOC((bs->size + 7) / 8, 1);
+   unsigned long y;
+
+   if (w == NULL) {
+      return CRYPT_MEM;
+   }
+
+   /* decode/store the bits */
+   for (y = 0; y < bs->size; y++) {
+      if (r[y]) {
+         SETBIT(w[y/8], 7-(y%8));
+      } else {
+         CLRBIT(w[y/8], 7-(y%8));
+      }
+   }
+
+   *out = w;
+   *outlen = bs->size;
+   return CRYPT_OK;
+}
+
+static LTC_INLINE int s_x509_check_version(unsigned long version)
+{
+   /* RFC5280 Ch. 4.1
+    *    Version  ::=  INTEGER  {  v1(0), v2(1), v3(2)  }
+    * We only accept x.509v3 */
+   if (version != 2) {
+      return CRYPT_PK_ASN1_ERROR;
+   }
+   return CRYPT_OK;
+}
+
+static int s_x509_get_version(const ltc_asn1_list *seq, unsigned long *version)
+{
+   int err;
+   ltc_asn1_list work[2];
+   LTC_SET_ASN1_CUSTOM_CONSTRUCTED(work, 0, LTC_ASN1_CL_CONTEXT_SPECIFIC, 0, work + 1);
+   LTC_SET_ASN1(work, 1, LTC_ASN1_SHORT_INTEGER, version,       1UL);
+   if ((err = der_decode_custom_type(seq->data, seq->size, work)) != CRYPT_OK) {
+      return err;
+   }
+   return s_x509_check_version(*version);
+}
+
+int x509_get_serial(const ltc_asn1_list *asn1, ltc_x509_string *serial)
+{
+   void *tmp;
+   unsigned long len;
+   int err;
+   /* We're printing the serial number as hex string.
+    * The length of the hex string is double the binary size + 3.
+    * 3 comes from: eventually sign + reserve byte if value is 0 + terminating NUL byte.
+    */
+   len = ltc_mp_unsigned_bin_size(asn1->data) * 2 + 3;
+   tmp = XMALLOC(len);
+   if (tmp == NULL)
+      return CRYPT_MEM;
+   if ((err = ltc_mp_tohex(asn1->data, tmp)) != CRYPT_OK) {
+      XFREE(tmp);
+      return err;
+   }
+   serial->type = LTC_X509_SERIAL;
+   serial->asn1 = asn1;
+   serial->str = tmp;
+   return CRYPT_OK;
+}
+
+static int s_x509_get_sig_alg(const ltc_asn1_list *seq, ltc_x509_signature_algorithm *sig_alg)
+{
+   int err;
+   ltc_asn1_list fake_sig_parent = {0};
+
+   fake_sig_parent.type = LTC_ASN1_SEQUENCE;
+   fake_sig_parent.child = (ltc_asn1_list*)seq;
+
+   if ((err = x509_get_sig_alg(&fake_sig_parent, sig_alg)) != CRYPT_OK) {
+      return err;
+   }
+   sig_alg->asn1 = seq;
+   return err;
+}
+
+/* Decode a Name according to RFC 5280
+ *
+ *    Name ::= CHOICE { -- only one possibility for now --
+ *      rdnSequence  RDNSequence }
+ *
+ *    RDNSequence ::= SEQUENCE OF RelativeDistinguishedName
+ *
+ *    RelativeDistinguishedName ::=
+ *      SET SIZE (1..MAX) OF AttributeTypeAndValue
+ *
+ *    AttributeTypeAndValue ::= SEQUENCE {
+ *      type     AttributeType,
+ *      value    AttributeValue }
+ *
+ *    AttributeType ::= OBJECT IDENTIFIER
+ *
+ *    AttributeValue ::= ANY -- DEFINED BY AttributeType
+ */
+static int s_x509_get_name(const ltc_asn1_list *seq, ltc_x509_name *name)
+{
+   ltc_asn1_list *set = seq->child;
+   st_oid_detail *name_elements;
+   ltc_x509_string *names;
+   unsigned long n, names_num = 0;
+   int err;
+   for (; set != NULL && set->type != LTC_ASN1_EOL; set = set->next) {
+      names_num++;
+   }
+   if (names_num == 0) {
+      name->asn1 = seq;
+      name->names = NULL;
+      name->names_num = 0;
+      return CRYPT_OK;
+   }
+   name_elements = XCALLOC(names_num, sizeof(*name_elements));
+   if (name_elements == NULL) {
+      return CRYPT_MEM;
+   }
+   names = XCALLOC(names_num, sizeof(*names));
+   if (names == NULL) {
+      err = CRYPT_MEM;
+      goto err_out;
+   }
+   for (n = 0; n < names_num; ++n) {
+      name_elements[n].str = &names[n];
+   }
+   if ((err = s_x509_get_name_process(seq->child, name_elements, names_num)) != CRYPT_OK) {
+      goto err_out;
+   }
+   name->asn1 = seq;
+   name->names = names;
+   name->names_num = names_num;
+err_out:
+   if (err != CRYPT_OK) {
+      s_free_x509_string_array(names, names_num);
+   }
+   XFREE(name_elements);
+   return err;
+}
+
+/* Decode the Validity according to RFC 5280
+ *
+ *    Validity ::= SEQUENCE {
+ *         notBefore      Time,
+ *         notAfter       Time }
+ *
+ *    Time ::= CHOICE {
+ *         utcTime        UTCTime,
+ *         generalTime    GeneralizedTime }
+ */
+
+static int s_x509_get_validity(const ltc_asn1_list *seq, ltc_x509_validity *validity)
+{
+   int n, err, ret;
+   ltc_asn1_list *source = seq->child;
+   ltc_x509_time *value = &validity->not_before;
+   validity->not_before.str = NULL;
+   validity->not_after.str = NULL;
+   for (n = 0; n < 2; ++n) {
+      if (source->type == LTC_ASN1_GENERALIZEDTIME) {
+         ltc_generalizedtime *gt = source->data;
+         value->utc = 0;
+         value->u.generalized = gt;
+         /* RFC5280 Ch. 4.1.2.5.2
+          *    GeneralizedTime values MUST be expressed in Greenwich Mean Time [...]
+          *    GeneralizedTime values MUST NOT include fractional seconds. */
+         if (gt->off_hh || gt->off_mm || gt->fs) {
+            err = CRYPT_PK_ASN1_ERROR;
+            goto err_out;
+         } else {
+            void *str = XMALLOC(24);
+            if (str == NULL) {
+               err = CRYPT_MEM;
+               goto err_out;
+            }
+            ret = snprintf(str, 24, "%04d-%02d-%02d %02d:%02d:%02dZ",
+                               gt->YYYY, gt->MM, gt->DD, gt->hh, gt->mm, gt->ss);
+            if (ret < 0 || ret >= 24) {
+               err = CRYPT_ERROR;
+               XFREE(str);
+               goto err_out;
+            }
+            value->str = str;
+            value->asn1 = source;
+         }
+      } else if (source->type == LTC_ASN1_UTCTIME) {
+         ltc_utctime* ut = source->data;
+         value->utc = 1;
+         value->u.utc = ut;
+         /* RFC5280 Ch. 4.1.2.5.1
+          *    UTCTime values MUST be expressed in Greenwich Mean Time */
+         if (ut->off_hh || ut->off_mm) {
+            err = CRYPT_PK_ASN1_ERROR;
+            goto err_out;
+         } else {
+            const char *YY;
+            void *str = XMALLOC(24);
+            if (str == NULL) {
+               err = CRYPT_MEM;
+               goto err_out;
+            }
+            /* Let's hope this software is not used after 2049...
+             * RFC5280 Ch. 4.1.2.5.1
+             *    Where YY is greater than or equal to 50, the year SHALL be
+             *    interpreted as 19YY; and
+             *
+             *    Where YY is less than 50, the year SHALL be interpreted as 20YY.
+             */
+            YY = ut->YY >= 50 ? "19" : "20";
+            ret = snprintf(str, 24, "%s%02d-%02d-%02d %02d:%02d:%02dZ",
+                               YY, ut->YY, ut->MM, ut->DD, ut->hh, ut->mm, ut->ss);
+            if (ret < 0 || ret >= 24) {
+               err = CRYPT_ERROR;
+               XFREE(str);
+               goto err_out;
+            }
+            value->str = str;
+            value->asn1 = source;
+         }
+      } else {
+         err = CRYPT_PK_ASN1_ERROR;
+         goto err_out;
+      }
+      source = source->next;
+      value = &validity->not_after;
+   }
+   return CRYPT_OK;
+err_out:
+   if (validity->not_after.str)
+      s_free(validity->not_after.str);
+   if (validity->not_before.str)
+      s_free(validity->not_before.str);
+   return err;
+}
+
+static int s_x509_check_spki(const ltc_asn1_list *seq, const ltc_pka_key *spki)
+{
+   LTC_UNUSED_PARAM(seq);
+   if (spki->id > LTC_PKA_UNDEF && spki->id < LTC_PKA_NUM) {
+      return CRYPT_OK;
+   }
+   return CRYPT_PK_INVALID_TYPE;
+}
+
+#ifndef S_IS_CONTEXT_SPECIFIC
+#define S_IS_CONTEXT_SPECIFIC
+static LTC_INLINE int s_is_context_specific(const ltc_asn1_list *seq)
+{
+   if (seq->type != LTC_ASN1_CUSTOM_TYPE)
+      return 0;
+   if (seq->klass != LTC_ASN1_CL_CONTEXT_SPECIFIC)
+      return 0;
+   return 1;
+}
+#endif
+
+#ifndef S_IS_CONTEXT_SPECIFIC_PRIMITIVE
+#define S_IS_CONTEXT_SPECIFIC_PRIMITIVE
+static LTC_INLINE int s_is_context_specific_primitive(const ltc_asn1_list *seq)
+{
+   if (!s_is_context_specific(seq))
+      return 0;
+   if (seq->pc != LTC_ASN1_PC_PRIMITIVE)
+      return 0;
+   return 1;
+}
+#endif
+
+static LTC_INLINE int s_is_context_specific_constructed(const ltc_asn1_list *seq)
+{
+   if (!s_is_context_specific(seq))
+      return 0;
+   if (seq->pc != LTC_ASN1_PC_CONSTRUCTED)
+      return 0;
+   return 1;
+}
+
+static int s_x509_get_optionals(const ltc_asn1_list *seq, ltc_x509_tbs_certificate *tbs_cert)
+{
+   switch (seq->tag) {
+      /* Context specific tags [1] and [2] are of type UniqueIdentifier
+       *
+       * RFC5280 Ch. 4.1.2.8.  Unique Identifiers
+       *    [...] Applications conforming to
+       *    this profile SHOULD be capable of parsing certificates that include
+       *    unique identifiers, but there are no processing requirements
+       *    associated with the unique identifiers.
+       *
+       * Good luck decoding them yourself :)
+       */
+      case 1:
+         if (!s_is_context_specific_primitive(seq))
+            return CRYPT_PK_ASN1_ERROR;
+         tbs_cert->issuer_uid = seq;
+         return CRYPT_OK;
+      case 2:
+         if (!s_is_context_specific_primitive(seq))
+            return CRYPT_PK_ASN1_ERROR;
+         tbs_cert->subject_uid = seq;
+         return CRYPT_OK;
+      case 3:
+         if (!s_is_context_specific_constructed(seq))
+            return CRYPT_PK_ASN1_ERROR;
+         return x509_get_extensions(seq->child, &tbs_cert->extensions);
+      default:
+         return CRYPT_PK_ASN1_ERROR;
+   }
+   return CRYPT_PK_ASN1_ERROR;
+}
+
+/* Decode a TBSCertificate according to RFC 5280
+ *
+ *  TBSCertificate  ::=  SEQUENCE  {
+ *       version         [0]  EXPLICIT Version DEFAULT v1,
+ *       serialNumber         CertificateSerialNumber,
+ *       signature            AlgorithmIdentifier,
+ *       issuer               Name,
+ *       validity             Validity,
+ *       subject              Name,
+ *       subjectPublicKeyInfo SubjectPublicKeyInfo,
+ *       issuerUniqueID  [1]  IMPLICIT UniqueIdentifier OPTIONAL,
+ *                            -- If present, version MUST be v2 or v3
+ *       subjectUniqueID [2]  IMPLICIT UniqueIdentifier OPTIONAL,
+ *                            -- If present, version MUST be v2 or v3
+ *       extensions      [3]  EXPLICIT Extensions OPTIONAL
+ *                            -- If present, version MUST be v3
+ *       }
+ */
+static int s_x509_import_tbs_certificate(const ltc_asn1_list *asn1, ltc_x509_tbs_certificate *tbs_cert)
+{
+   int err;
+   der_flexi_check flexi_should[11];
+   LTC_SET_DER_FLEXI_HANDLER_OPT(flexi_should, 0, LTC_ASN1_CUSTOM_TYPE, s_x509_get_version, &tbs_cert->version);
+   LTC_SET_DER_FLEXI_HANDLER(flexi_should, 1, LTC_ASN1_INTEGER, x509_get_serial, &tbs_cert->serial_number);
+   LTC_SET_DER_FLEXI_HANDLER(flexi_should, 2, LTC_ASN1_SEQUENCE, s_x509_get_sig_alg, &tbs_cert->signature_algorithm);
+   LTC_SET_DER_FLEXI_HANDLER(flexi_should, 3, LTC_ASN1_SEQUENCE, s_x509_get_name, &tbs_cert->issuer);
+   LTC_SET_DER_FLEXI_HANDLER(flexi_should, 4, LTC_ASN1_SEQUENCE, s_x509_get_validity, &tbs_cert->validity);
+   LTC_SET_DER_FLEXI_HANDLER(flexi_should, 5, LTC_ASN1_SEQUENCE, s_x509_get_name, &tbs_cert->subject);
+   LTC_SET_DER_FLEXI_HANDLER(flexi_should, 6, LTC_ASN1_SEQUENCE, s_x509_check_spki, &tbs_cert->subject_public_key_info);
+   LTC_SET_DER_FLEXI_HANDLER_OPT(flexi_should, 7, LTC_ASN1_CUSTOM_TYPE, s_x509_get_optionals, tbs_cert);
+   LTC_SET_DER_FLEXI_HANDLER_OPT(flexi_should, 8, LTC_ASN1_CUSTOM_TYPE, s_x509_get_optionals, tbs_cert);
+   LTC_SET_DER_FLEXI_HANDLER_OPT(flexi_should, 9, LTC_ASN1_CUSTOM_TYPE, s_x509_get_optionals, tbs_cert);
+   LTC_SET_DER_FLEXI_CHECK(flexi_should, 10, LTC_ASN1_EOL, NULL);
+   if ((err = der_flexi_sequence_cmp(asn1, flexi_should)) == CRYPT_OK) {
+      tbs_cert->asn1 = asn1;
+      err = s_x509_check_version(tbs_cert->version);
+   }
+   return err;
+}
+
+/**
+  Import an X.509 certificate from ASN.1 DER-encoded data.
+
+  @param asn1_cert   Pointer to the ASN.1 DER-encoded data.
+  @param asn1_len    Length of the ASN.1 DER-encoded data.
+  @param out         The resulting X.509 certificate.
+  @return CRYPT_OK if successful.
+*/
+int x509_import(const unsigned char *asn1_cert, unsigned long asn1_len, const ltc_x509_certificate **out)
+{
+   ltc_x509_certificate *cert;
+   ltc_asn1_list *root, *tbs_cert, *sig_alg, *sig;
+   int err;
+   cert = XCALLOC(1, sizeof(*cert));
+   if (cert == NULL) {
+      return CRYPT_MEM;
+   }
+   if ((err = x509_import_spki(asn1_cert, asn1_len, &cert->tbs_certificate.subject_public_key_info, &root)) != CRYPT_OK) {
+      goto err_out;
+   }
+
+   tbs_cert = root->child;
+   sig_alg = tbs_cert->next;
+   sig = sig_alg->next;
+   cert->signature.asn1 = sig;
+   cert->asn1 = root;
+
+   if ((err = s_x509_import_tbs_certificate(tbs_cert, &cert->tbs_certificate)) != CRYPT_OK) {
+      goto err_out;
+   }
+
+   if ((err = s_x509_get_sig_alg(sig_alg, &cert->signature_algorithm)) != CRYPT_OK) {
+      goto err_out;
+   }
+   if ((err = s_bit_string_to_raw_bit_string(sig, (unsigned char**)&cert->signature.signature, &cert->signature.signature_len))) {
+      goto err_out;
+   }
+   *out = cert;
+   return err;
+err_out:
+   x509_free((const ltc_x509_certificate **)&cert);
+   return err;
+}
+
+#ifdef LTC_PEM
+extern const struct pem_header_id pem_std_headers[];
+
+static int s_x509_import_pem(struct get_char *g, unsigned long *pem_len, const ltc_x509_certificate **out)
+{
+   int err;
+   struct pem_headers hdr = { .id = &pem_std_headers[0] };
+   unsigned long len = 0;
+   unsigned char *asn1_cert = NULL;
+   if ((err = pem_read((void**)&asn1_cert, &len, &hdr, g)) != CRYPT_OK) {
+      return err;
+   }
+
+   err = x509_import(asn1_cert, len, out);
+
+   *pem_len = g->total_read;
+
+   XFREE(asn1_cert);
+
+   return err;
+}
+
+/**
+  Import an X.509 certificate from PEM-encoded data.
+
+  @param pem         Pointer to the PEM-encoded data.
+  @param pem_len     [in] Length of the PEM-encoded data.
+                     [out] The length of the data used.
+  @param out         The resulting X.509 certificate.
+  @return CRYPT_OK if successful.
+*/
+int x509_import_pem(const char *pem, unsigned long *pem_len, const ltc_x509_certificate **out)
+{
+   struct get_char g = pem_get_char_init(pem, *pem_len);
+   return s_x509_import_pem(&g, pem_len, out);
+}
+
+#ifndef LTC_NO_FILE
+/**
+  Import an X.509 certificate from PEM-encoded data from a FILE handle.
+
+  @param f     The FILE handle.
+  @param out   The resulting X.509 certificate.
+  @return CRYPT_OK if successful.
+*/
+int x509_import_pem_filehandle(FILE *f, const ltc_x509_certificate **out)
+{
+   unsigned long pem_len;
+   struct get_char g = pem_get_char_init_filehandle(f);
+   return s_x509_import_pem(&g, &pem_len, out);
+}
+#endif /* LTC_NO_FILE */
+#endif /* LTC_PEM */
+
+static void s_free_x509_name(const ltc_x509_name *name)
+{
+   s_free_x509_string_array(name->names, name->names_num);
+}
+
+static void s_free_x509_tbs_cert(const ltc_x509_tbs_certificate *tbs_cert)
+{
+   x509_free_extensions(&tbs_cert->extensions);
+   s_free_x509_name(&tbs_cert->subject);
+   s_free(tbs_cert->validity.not_after.str);
+   s_free(tbs_cert->validity.not_before.str);
+   s_free_x509_name(&tbs_cert->issuer);
+   s_free(tbs_cert->serial_number.str);
+   pka_key_free((void*)&tbs_cert->subject_public_key_info);
+}
+
+/**
+  Free an X.509 certificate from memory
+
+  @param cert   The X.509 certificate to free.
+*/
+void x509_free(const ltc_x509_certificate **cert)
+{
+   const ltc_x509_certificate *c;
+   LTC_ARGCHKVD(cert != NULL);
+   c = *cert;
+   LTC_ARGCHKVD(c != NULL);
+   s_free(c->signature.signature);
+   s_free_x509_tbs_cert(&c->tbs_certificate);
+   der_free_sequence_flexi((void*)c->asn1);
+   s_free(c);
+   *cert = NULL;
+}
+
+#undef st_oid_detail
+
+#endif /* LTC_DER */

+ 194 - 0
src/pk/asn1/x509/x509_utils.c

@@ -0,0 +1,194 @@
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis */
+/* SPDX-License-Identifier: Unlicense */
+#include "tomcrypt_private.h"
+
+/**
+  @file x509_utils.c
+  More X.509 APIs, Steffen Jaeckel
+*/
+
+typedef struct pka_sig_args {
+   int  hash_idx;
+   const ltc_rsa_parameters *rsa_params;
+} pka_sig_args;
+
+static LTC_INLINE int s_pka_verify(const unsigned char *msg, unsigned long msglen,
+                                   const unsigned char *sig, unsigned long siglen,
+                                          pka_sig_args  *sig_args,
+                                                   int *stat,
+                                   const   ltc_pka_key *key)
+{
+   ltc_rsa_op_parameters rsa_params = { 0 };
+   ltc_ecc_sig_opts ecc_opts = {
+                                .type = LTC_ECCSIG_ANSIX962
+   };
+   switch (key->id) {
+#ifdef LTC_MRSA
+      case LTC_PKA_RSA:
+         rsa_params.params.hash_alg = hash_descriptor[sig_args->hash_idx].name;
+         rsa_params.padding = LTC_PKCS_1_V1_5;
+         /* RSA Keys usually use PKCS#1 v1.5 padding */
+         return rsa_verify_hash_v2(sig, siglen, msg, msglen, &rsa_params, stat, &key->u.rsa);
+      case LTC_PKA_RSA_PSS:
+      {
+         if (sig_args->rsa_params == NULL)
+            return CRYPT_PK_INVALID_TYPE;
+         rsa_params.params = *sig_args->rsa_params;
+         rsa_params.padding = LTC_PKCS_1_PSS;
+         return rsa_verify_hash_v2(sig, siglen, msg, msglen, &rsa_params, stat, &key->u.rsa);
+      }
+#endif
+#ifdef LTC_MDSA
+      case LTC_PKA_DSA:
+         return dsa_verify_hash(sig, siglen, msg, msglen, stat, &key->u.dsa);
+#endif
+#ifdef LTC_MECC
+      case LTC_PKA_EC:
+         return ecc_verify_hash_v2(sig, siglen, msg, msglen, &ecc_opts, stat, &key->u.ecc);
+#endif
+#ifdef LTC_CURVE25519
+      case LTC_PKA_ED25519:
+         return ed25519_verify(msg, msglen, sig, siglen, stat, &key->u.ed25519);
+#endif
+      default:
+         return CRYPT_PK_INVALID_TYPE;
+   }
+}
+
+/* RFC5280 Ch. 4.1.1.2.  signatureAlgorithm
+ * [...]
+ *    This field MUST contain the same algorithm identifier as the
+ *    signature field in the sequence tbsCertificate (Section 4.1.2.3).
+ */
+static LTC_INLINE int s_signature_algorithms_equal(const ltc_x509_signature_algorithm *a, const ltc_x509_signature_algorithm *b)
+{
+   if (a->pka != b->pka)
+      return 0;
+   if (a->pka == LTC_PKA_RSA_PSS) {
+      if (!rsa_params_equal(&a->u.rsa_params, &b->u.rsa_params))
+         return 0;
+   }
+   return 1;
+}
+
+/**
+  Check whether an X.509 certificate is signed by a specific PKA key.
+
+  @param cert   The X.509 certificate to validate.
+  @param key    The key to validate the certificate with.
+  @param stat   [out] The result of the signature comparison: 1==valid, 0==invalid.
+  @return CRYPT_OK if successful
+*/
+int x509_cert_is_signed_by(const ltc_x509_certificate *cert, const ltc_pka_key *key, int *stat)
+{
+   unsigned char buf[MAXBLOCKSIZE], *msg;
+   unsigned long msglen = sizeof(buf);
+   const char *hashalg;
+   pka_sig_args sig_args = {0};
+   int err;
+
+   LTC_ARGCHK(cert != NULL);
+   LTC_ARGCHK(key  != NULL);
+   LTC_ARGCHK(stat != NULL);
+
+   *stat = 0;
+   /* Check that signatureAlgorithms match AND the key must be appropriate. */
+   if (!s_signature_algorithms_equal(&cert->signature_algorithm, &cert->tbs_certificate.signature_algorithm)
+         || (cert->signature_algorithm.pka != key->id)) {
+      return CRYPT_PK_TYPE_MISMATCH;
+   }
+   sig_args.hash_idx = -1;
+   if (key->id == LTC_PKA_ED25519) {
+      msg = cert->tbs_certificate.asn1->data;
+      msglen = cert->tbs_certificate.asn1->size;
+   } else {
+      if (key->id == LTC_PKA_RSA_PSS) {
+         if (cert->signature_algorithm.u.rsa_params.pss_oaep) {
+            sig_args.rsa_params = &cert->signature_algorithm.u.rsa_params;
+            if (key->u.rsa.params.pss_oaep && !rsa_params_equal(&key->u.rsa.params, sig_args.rsa_params)) {
+               return CRYPT_PK_TYPE_MISMATCH;
+            }
+         } else if (key->u.rsa.params.pss_oaep) {
+            sig_args.rsa_params = &key->u.rsa.params;
+         } else {
+            return CRYPT_PK_TYPE_MISMATCH;
+         }
+         hashalg = sig_args.rsa_params->hash_alg;
+      } else {
+         hashalg = cert->signature_algorithm.u.hash;
+      }
+      if ((sig_args.hash_idx = find_hash(hashalg)) == -1) {
+         return CRYPT_INVALID_HASH;
+      }
+      if ((err = hash_memory(sig_args.hash_idx, cert->tbs_certificate.asn1->data, cert->tbs_certificate.asn1->size, buf, &msglen)) != CRYPT_OK) {
+         return err;
+      }
+      msg = buf;
+   }
+   if ((err = s_pka_verify(msg, msglen, cert->signature.signature, cert->signature.signature_len/8, &sig_args, stat, key)) != CRYPT_OK) {
+      return err;
+   }
+   return err;
+}
+
+/**
+  Compare two X.509 NAME instances.
+
+  @param a   A X.509 NAME.
+  @param b   A X.509 NAME.
+  @return 1 if equal, 0 if unequal.
+*/
+int x509_cmp_name(const ltc_x509_name *a, const ltc_x509_name *b)
+{
+   LTC_ARGCHK(a != NULL);
+   LTC_ARGCHK(b != NULL);
+
+   if (a == b)
+      return 1;
+   if (a->asn1->size != b->asn1->size)
+      return 0;
+   return XMEMCMP(a->asn1->data, b->asn1->data, a->asn1->size) == 0 ? 1 : 0;
+}
+
+/**
+  Retrieve a specific component from an X.509 NAME.
+
+  @param name  A X.509 NAME.
+  @param type  The type of the component to retrieve.
+  @param str   The result.
+  @return CRYPT_OK if successful.
+*/
+int x509_name_detail_get(const ltc_x509_name *name, ltc_x509_details type, const ltc_x509_string **str)
+{
+   unsigned long n;
+
+   LTC_ARGCHK(name != NULL);
+   LTC_ARGCHK(str != NULL);
+
+   for (n = 0; n < name->names_num; ++n) {
+      if (name->names[n].type == type) {
+         *str = &name->names[n];
+         return CRYPT_OK;
+      }
+   }
+   *str = NULL;
+   return CRYPT_INVALID_ARG;
+}
+
+const char *x509_name_detail_desc(ltc_x509_details type)
+{
+   const char *standard_attributes[] = {
+                                        "Serial",
+                                        "CommonName",
+                                        "CountryCode",
+                                        "Locality",
+                                        "State or Province",
+                                        "Organisation",
+                                        "OrganisationalUnit",
+                                        "EmailAddress",
+   };
+   if (type <= LTC_X509_EMAIL) {
+      return standard_attributes[type];
+   }
+   return "Unknown";
+}