Browse Source

Refactor SubjectPublicKeyInfo import

Slightly minimize both space and time when importing a
SubjectPublicKeyInfo. Time for ECC keys stays the same.

Those tests were done with X.509 support already available, but later these
commits were split up to be independent of the X.509 feature.

Running the entire set of pem files through `x509_verify` via [0]
resp. the timing app via [1] resulted in the following data:

Before this patch:

[0]
```
==1031519== HEAP SUMMARY:
==1031519==     in use at exit: 0 bytes in 0 blocks
==1031519==   total heap usage: 424,057 allocs, 424,057 frees, 73,527,730 bytes allocated
```

[1]
```
x509 cert-rsa-pss.pem    :     50021 cycles
x509 LTC_CA.pem          :     10335 cycles
x509 LTC_S0.pem          :     47284 cycles
x509 LTC_SS0.pem         :     36687 cycles
x509 secp384r1.pem       :   1985416 cycles
x509 secp521r1.pem       :   3287773 cycles
x509 LTC_SSS0.pem        :     25086 cycles
x509 secp224r1.pem       :    775807 cycles
```

After this patch:

[0]
```
==1043548== HEAP SUMMARY:
==1043548==     in use at exit: 0 bytes in 0 blocks
==1043548==   total heap usage: 337,244 allocs, 337,244 frees, 65,047,463 bytes allocated
```

[1]
```
x509 cert-rsa-pss.pem    :     32568 cycles
x509 LTC_CA.pem          :      5478 cycles
x509 LTC_S0.pem          :     36093 cycles
x509 LTC_SS0.pem         :     23351 cycles
x509 secp384r1.pem       :   1984030 cycles
x509 secp521r1.pem       :   3303396 cycles
x509 LTC_SSS0.pem        :     13220 cycles
x509 secp224r1.pem       :    781534 cycles
```

[0] find tests/x509 -name '*.pem' -exec valgrind --leak-check=full --show-leak-kinds=all './x509_verify' {} \+
[1] ./timing x509

Signed-off-by: Steffen Jaeckel <[email protected]>
Steffen Jaeckel 3 months ago
parent
commit
a79274a722

+ 6 - 4
src/headers/tomcrypt_private.h

@@ -458,6 +458,8 @@ int rsa_make_key_bn_e(prng_state *prng, int wprng, int size, void *e,
                       rsa_key *key); /* used by op-tee */
                       rsa_key *key); /* used by op-tee */
 int rsa_import_pkcs1(const unsigned char *in, unsigned long inlen, rsa_key *key);
 int rsa_import_pkcs1(const unsigned char *in, unsigned long inlen, rsa_key *key);
 int rsa_import_pkcs8_asn1(ltc_asn1_list *alg_id, ltc_asn1_list *priv_key, rsa_key *key);
 int rsa_import_pkcs8_asn1(ltc_asn1_list *alg_id, ltc_asn1_list *priv_key, rsa_key *key);
+int rsa_import_spki(const unsigned char *in, unsigned long inlen, rsa_key *key);
+int rsa_decode_parameters(const ltc_asn1_list *parameters, ltc_rsa_parameters *rsa_params);
 #endif /* LTC_MRSA */
 #endif /* LTC_MRSA */
 
 
 /* ---- DH Routines ---- */
 /* ---- DH Routines ---- */
@@ -617,8 +619,8 @@ int dsa_int_validate(const dsa_key *key, int *stat);
 int dsa_int_validate_xy(const dsa_key *key, int *stat);
 int dsa_int_validate_xy(const dsa_key *key, int *stat);
 int dsa_int_validate_pqg(const dsa_key *key, int *stat);
 int dsa_int_validate_pqg(const dsa_key *key, int *stat);
 int dsa_int_validate_primes(const dsa_key *key, int *stat);
 int dsa_int_validate_primes(const dsa_key *key, int *stat);
-int dsa_import_pkcs1(const unsigned char *in, unsigned long inlen, dsa_key *key);
 int dsa_import_pkcs8_asn1(ltc_asn1_list *alg_id, ltc_asn1_list *priv_key, dsa_key *key);
 int dsa_import_pkcs8_asn1(ltc_asn1_list *alg_id, ltc_asn1_list *priv_key, dsa_key *key);
+int dsa_import_spki(const unsigned char *in, unsigned long inlen, dsa_key *key);
 #endif /* LTC_MDSA */
 #endif /* LTC_MDSA */
 
 
 
 
@@ -733,17 +735,17 @@ int der_teletex_value_decode(int v);
 
 
 int der_utf8_valid_char(const wchar_t c);
 int der_utf8_valid_char(const wchar_t c);
 
 
-typedef int (*public_key_decode_cb)(const unsigned char *in, unsigned long inlen, void *ctx);
+typedef int (*public_key_decode_cb)(const unsigned char *in, unsigned long inlen, void *key);
 
 
 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,
                                             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 *key);
 int x509_decode_spki(const unsigned char *in, unsigned long inlen, ltc_asn1_list **out, const ltc_asn1_list **spki);
 int x509_decode_spki(const unsigned char *in, unsigned long inlen, ltc_asn1_list **out, const ltc_asn1_list **spki);
 int x509_process_public_key_from_spki(const unsigned char *in, unsigned long inlen,
 int x509_process_public_key_from_spki(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,
                                       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 *key);
 
 
 /* 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,

+ 5 - 5
src/pk/asn1/x509/x509_decode_public_key_from_certificate.c

@@ -24,7 +24,7 @@
 int x509_process_public_key_from_spki(const unsigned char *in, unsigned long inlen,
 int x509_process_public_key_from_spki(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,
                                       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 *key)
 {
 {
    int err;
    int err;
    unsigned char *tmpbuf = NULL;
    unsigned char *tmpbuf = NULL;
@@ -34,7 +34,7 @@ int x509_process_public_key_from_spki(const unsigned char *in, unsigned long inl
    LTC_ARGCHK(callback  != NULL);
    LTC_ARGCHK(callback  != NULL);
 
 
    if (algorithm == LTC_OID_EC) {
    if (algorithm == LTC_OID_EC) {
-      err = callback(in, inlen, ctx);
+      err = callback(in, inlen, key);
    } else {
    } else {
 
 
       tmpbuf_len = inlen;
       tmpbuf_len = inlen;
@@ -47,7 +47,7 @@ int x509_process_public_key_from_spki(const unsigned char *in, unsigned long inl
                                                 algorithm, tmpbuf, &tmpbuf_len,
                                                 algorithm, tmpbuf, &tmpbuf_len,
                                                 param_type, parameters, parameters_len);
                                                 param_type, parameters, parameters_len);
       if (err == CRYPT_OK) {
       if (err == CRYPT_OK) {
-         err = callback(tmpbuf, tmpbuf_len, ctx);
+         err = callback(tmpbuf, tmpbuf_len, key);
       }
       }
    }
    }
 
 
@@ -73,7 +73,7 @@ int x509_process_public_key_from_spki(const unsigned char *in, unsigned long inl
 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,
                                             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 *key)
 {
 {
    int err;
    int err;
    ltc_asn1_list *decoded_list;
    ltc_asn1_list *decoded_list;
@@ -90,7 +90,7 @@ int x509_decode_public_key_from_certificate(const unsigned char *in, unsigned lo
    err = x509_process_public_key_from_spki(spki->data, spki->size,
    err = x509_process_public_key_from_spki(spki->data, spki->size,
                                            algorithm, param_type,
                                            algorithm, param_type,
                                            parameters, parameters_len,
                                            parameters, parameters_len,
-                                           callback, ctx);
+                                           callback, key);
 
 
    if (decoded_list) der_free_sequence_flexi(decoded_list);
    if (decoded_list) der_free_sequence_flexi(decoded_list);
 
 

+ 37 - 11
src/pk/asn1/x509/x509_import_spki.c

@@ -9,22 +9,48 @@
 
 
 #ifdef LTC_DER
 #ifdef LTC_DER
 
 
-typedef int (*import_fn)(const unsigned char *, unsigned long, void*);
+typedef int (*import_fn)(const unsigned char *, unsigned long, void *);
 
 
-static const import_fn s_import_x509_fns[LTC_PKA_NUM] = {
+#ifdef LTC_CURVE25519
+static int s_x25519_import_pub(const unsigned char *in, unsigned long inlen, void *key)
+{
+   return x25519_import_raw(in, inlen, PK_PUBLIC, key);
+}
+static int s_x25519_import_spki(const unsigned char *in, unsigned long inlen, void *key)
+{
+   return x509_process_public_key_from_spki(in, inlen,
+                                            LTC_OID_X25519,
+                                            LTC_ASN1_EOL, NULL, NULL,
+                                            s_x25519_import_pub, key);
+}
+
+static int s_ed25519_import_pub(const unsigned char *in, unsigned long inlen, void *key)
+{
+   return ed25519_import_raw(in, inlen, PK_PUBLIC, key);
+}
+static int s_ed25519_import_spki(const unsigned char *in, unsigned long inlen, void *key)
+{
+   return x509_process_public_key_from_spki(in, inlen,
+                                            LTC_OID_ED25519,
+                                            LTC_ASN1_EOL, NULL, NULL,
+                                            s_ed25519_import_pub, key);
+}
+#endif
+
+static const import_fn s_import_spki_fns[LTC_PKA_NUM] = {
 #ifdef LTC_MRSA
 #ifdef LTC_MRSA
-                                                [LTC_PKA_RSA] = (import_fn)rsa_import_x509,
-                                                [LTC_PKA_RSA_PSS] = (import_fn)rsa_import_x509,
+                                                [LTC_PKA_RSA] = (import_fn)rsa_import_spki,
+                                                [LTC_PKA_RSA_PSS] = (import_fn)rsa_import_spki,
 #endif
 #endif
 #ifdef LTC_MDSA
 #ifdef LTC_MDSA
-                                                [LTC_PKA_DSA] = (import_fn)dsa_import,
+                                                [LTC_PKA_DSA] = (import_fn)dsa_import_spki,
 #endif
 #endif
 #ifdef LTC_MECC
 #ifdef LTC_MECC
-                                                [LTC_PKA_EC] = (import_fn)ecc_import_x509,
+                                                [LTC_PKA_EC] = (import_fn)ecc_import_subject_public_key_info,
 #endif
 #endif
 #ifdef LTC_CURVE25519
 #ifdef LTC_CURVE25519
-                                                [LTC_PKA_X25519] = (import_fn)x25519_import_x509,
-                                                [LTC_PKA_ED25519] = (import_fn)ed25519_import_x509,
+                                                [LTC_PKA_X25519] = (import_fn)s_x25519_import_spki,
+                                                [LTC_PKA_ED25519] = (import_fn)s_ed25519_import_spki,
 #endif
 #endif
 };
 };
 
 
@@ -41,12 +67,12 @@ int x509_import_spki(const unsigned char *asn1_cert, unsigned long asn1_len, ltc
       goto err_out;
       goto err_out;
    }
    }
    if (pka < 0
    if (pka < 0
-         || pka > LTC_ARRAY_SIZE(s_import_x509_fns)
-         || s_import_x509_fns[pka] == NULL) {
+         || pka > LTC_ARRAY_SIZE(s_import_spki_fns)
+         || s_import_spki_fns[pka] == NULL) {
       err = CRYPT_PK_INVALID_TYPE;
       err = CRYPT_PK_INVALID_TYPE;
       goto err_out;
       goto err_out;
    }
    }
-   if ((err = s_import_x509_fns[pka](asn1_cert, asn1_len, &k->u)) == CRYPT_OK) {
+   if ((err = s_import_spki_fns[pka](spki->data, spki->size, &k->u)) == CRYPT_OK) {
       k->id = pka;
       k->id = pka;
    }
    }
 err_out:
 err_out:

+ 45 - 13
src/pk/dsa/dsa_import.c

@@ -9,7 +9,7 @@
 
 
 #ifdef LTC_MDSA
 #ifdef LTC_MDSA
 
 
-int dsa_import_pkcs1(const unsigned char *in, unsigned long inlen, dsa_key *key)
+static int s_dsa_import_pkcs1(const unsigned char *in, unsigned long inlen, dsa_key *key)
 {
 {
    int           err;
    int           err;
    unsigned long zero = 0;
    unsigned long zero = 0;
@@ -33,7 +33,7 @@ static int s_dsa_import_y(const unsigned char *in, unsigned long inlen, dsa_key
    return der_decode_integer(in, inlen, key->y);
    return der_decode_integer(in, inlen, key->y);
 }
 }
 
 
-LTC_INLINE static int s_dsa_set_params(dsa_key *key, ltc_asn1_list *params)
+static LTC_INLINE int s_dsa_set_params(dsa_key *key, ltc_asn1_list *params)
 {
 {
    LTC_SET_ASN1(params, 0, LTC_ASN1_INTEGER, key->p, 1UL);
    LTC_SET_ASN1(params, 0, LTC_ASN1_INTEGER, key->p, 1UL);
    LTC_SET_ASN1(params, 1, LTC_ASN1_INTEGER, key->q, 1UL);
    LTC_SET_ASN1(params, 1, LTC_ASN1_INTEGER, key->q, 1UL);
@@ -41,6 +41,24 @@ LTC_INLINE static int s_dsa_set_params(dsa_key *key, ltc_asn1_list *params)
    return 3;
    return 3;
 }
 }
 
 
+static LTC_INLINE int s_dsa_validate(dsa_key *key)
+{
+   int           err, stat;
+   key->qord = ltc_mp_unsigned_bin_size(key->q);
+
+   /* quick p, q, g validation, without primality testing
+    * + x, y validation */
+   if ((err = dsa_int_validate(key, &stat)) != CRYPT_OK) {
+      return err;
+   }
+
+   if (stat == 0) {
+      return CRYPT_INVALID_PACKET;
+   }
+
+   return CRYPT_OK;
+}
+
 static int s_dsa_import_spki(const unsigned char *in, unsigned long inlen, dsa_key *key)
 static int s_dsa_import_spki(const unsigned char *in, unsigned long inlen, dsa_key *key)
 {
 {
    int err;
    int err;
@@ -72,6 +90,28 @@ LBL_ERR:
    return err;
    return err;
 }
 }
 
 
+int dsa_import_spki(const unsigned char *in, unsigned long inlen, dsa_key *key)
+{
+   int           err;
+
+   LTC_ARGCHK(in  != NULL);
+
+   /* init key */
+   if ((err = dsa_int_init(key)) != CRYPT_OK) return err;
+
+   if ((err = s_dsa_import_spki(in, inlen, key)) != CRYPT_OK) {
+      goto LBL_ERR;
+   }
+   if ((err = s_dsa_validate(key)) != CRYPT_OK) {
+      goto LBL_ERR;
+   }
+
+   return CRYPT_OK;
+LBL_ERR:
+   dsa_free(key);
+   return err;
+}
+
 static int s_dsa_import_x509(const unsigned char *in, unsigned long inlen, dsa_key *key)
 static int s_dsa_import_x509(const unsigned char *in, unsigned long inlen, dsa_key *key)
 {
 {
    int err;
    int err;
@@ -100,7 +140,7 @@ static int s_dsa_import_x509(const unsigned char *in, unsigned long inlen, dsa_k
 */
 */
 int dsa_import(const unsigned char *in, unsigned long inlen, dsa_key *key)
 int dsa_import(const unsigned char *in, unsigned long inlen, dsa_key *key)
 {
 {
-   int           err, stat;
+   int           err;
    unsigned char flags[1];
    unsigned char flags[1];
 
 
    LTC_ARGCHK(in  != NULL);
    LTC_ARGCHK(in  != NULL);
@@ -148,7 +188,7 @@ int dsa_import(const unsigned char *in, unsigned long inlen, dsa_key *key)
        }
        }
    }
    }
 
 
-   if (dsa_import_pkcs1(in, inlen, key) == CRYPT_OK) {
+   if (s_dsa_import_pkcs1(in, inlen, key) == CRYPT_OK) {
       goto LBL_OK;
       goto LBL_OK;
    }
    }
    if (s_dsa_import_spki(in, inlen, key) == CRYPT_OK) {
    if (s_dsa_import_spki(in, inlen, key) == CRYPT_OK) {
@@ -159,15 +199,7 @@ int dsa_import(const unsigned char *in, unsigned long inlen, dsa_key *key)
    }
    }
 
 
 LBL_OK:
 LBL_OK:
-   key->qord = ltc_mp_unsigned_bin_size(key->q);
-
-   /* quick p, q, g validation, without primality testing
-    * + x, y validation */
-   if ((err = dsa_int_validate(key, &stat)) != CRYPT_OK) {
-      goto LBL_ERR;
-   }
-   if (stat == 0) {
-      err = CRYPT_INVALID_PACKET;
+   if ((err = s_dsa_validate(key)) != CRYPT_OK) {
       goto LBL_ERR;
       goto LBL_ERR;
    }
    }
 
 

+ 5 - 10
src/pk/rsa/rsa_import.c

@@ -99,7 +99,8 @@ int rsa_import(const unsigned char *in, unsigned long inlen, rsa_key *key)
    LTC_ARGCHK(key         != NULL);
    LTC_ARGCHK(key         != NULL);
    LTC_ARGCHK(ltc_mp.name != NULL);
    LTC_ARGCHK(ltc_mp.name != NULL);
 
 
-   if ((err = rsa_import_x509(in, inlen, key)) == CRYPT_OK) { /* SubjectPublicKeyInfo format */
+   /* SubjectPublicKeyInfo or X.509 certificate format */
+   if (rsa_import_x509(in, inlen, key) == CRYPT_OK) {
       return CRYPT_OK;
       return CRYPT_OK;
    }
    }
 
 
@@ -107,15 +108,9 @@ int rsa_import(const unsigned char *in, unsigned long inlen, rsa_key *key)
    if ((err = rsa_init(key)) != CRYPT_OK) {
    if ((err = rsa_init(key)) != CRYPT_OK) {
       return err;
       return err;
    }
    }
-
-   if ((err = x509_process_public_key_from_spki(in, inlen,
-                                                LTC_OID_RSA,
-                                                LTC_ASN1_NULL, NULL, NULL,
-                                                (public_key_decode_cb)s_rsa_decode, key)) != CRYPT_OK) {
-      /* not SSL public key, try to match against PKCS #1 standards */
-      if ((err = rsa_import_pkcs1(in, inlen, key)) != CRYPT_OK) {
-         rsa_free(key);
-      }
+   /* Try to match against PKCS #1 standards */
+   if ((err = rsa_import_pkcs1(in, inlen, key)) != CRYPT_OK) {
+      rsa_free(key);
    }
    }
 
 
    return err;
    return err;

+ 99 - 45
src/pk/rsa/rsa_import_x509.c

@@ -77,51 +77,44 @@ static LTC_INLINE void s_rsa_pss_parameters_data_setup(rsa_pss_parameters_data *
    }
    }
 }
 }
 
 
-int rsa_decode_parameters(const ltc_asn1_list *parameters, ltc_rsa_parameters *rsa_params)
+static int s_rsa_decode_parameters(const rsa_pss_parameters_data *d, ltc_rsa_parameters *rsa_params)
 {
 {
-   rsa_pss_parameters_data d;
    unsigned long n;
    unsigned long n;
    enum ltc_oid_id oid_id;
    enum ltc_oid_id oid_id;
    int           err, idx;
    int           err, idx;
 
 
-   s_rsa_pss_parameters_data_setup(&d);
-
-   if ((err = der_decode_sequence(parameters->data, parameters->size, d.params, 4)) != CRYPT_OK) {
-      return err;
-   }
-
    rsa_params->saltlen = 20;
    rsa_params->saltlen = 20;
    rsa_params->hash_alg = rsa_params->mgf1_hash_alg = "sha1";
    rsa_params->hash_alg = rsa_params->mgf1_hash_alg = "sha1";
 
 
    for (n = 0; n < 4; ++n) {
    for (n = 0; n < 4; ++n) {
-      if (d.params[n].used == 0)
+      if (d->params[n].used == 0)
          continue;
          continue;
       switch (n) {
       switch (n) {
          case 0:
          case 0:
-            idx = find_hash_oid(d.hash_alg->data, d.hash_alg->size);
+            idx = find_hash_oid(d->hash_alg->data, d->hash_alg->size);
             if (idx == -1) {
             if (idx == -1) {
                return CRYPT_INVALID_HASH;
                return CRYPT_INVALID_HASH;
             }
             }
             rsa_params->hash_alg = hash_descriptor[idx].name;
             rsa_params->hash_alg = hash_descriptor[idx].name;
             break;
             break;
          case 1:
          case 1:
-            if ((err = pk_get_oid_from_asn1(&d.mgf[0], &oid_id)) != CRYPT_OK) {
+            if ((err = pk_get_oid_from_asn1(&d->mgf[0], &oid_id)) != CRYPT_OK) {
                return err;
                return err;
             }
             }
             if (oid_id != LTC_OID_RSA_MGF1) {
             if (oid_id != LTC_OID_RSA_MGF1) {
                return CRYPT_PK_ASN1_ERROR;
                return CRYPT_PK_ASN1_ERROR;
             }
             }
-            idx = find_hash_oid(d.mgf_hash_alg->data, d.mgf_hash_alg->size);
+            idx = find_hash_oid(d->mgf_hash_alg->data, d->mgf_hash_alg->size);
             if (idx == -1) {
             if (idx == -1) {
                return CRYPT_INVALID_HASH;
                return CRYPT_INVALID_HASH;
             }
             }
             rsa_params->mgf1_hash_alg = hash_descriptor[idx].name;
             rsa_params->mgf1_hash_alg = hash_descriptor[idx].name;
             break;
             break;
          case 2:
          case 2:
-            rsa_params->saltlen = d.salt_length;
+            rsa_params->saltlen = d->salt_length;
             break;
             break;
          case 3:
          case 3:
-            if (d.trailer_field != 1) {
+            if (d->trailer_field != 1) {
                return CRYPT_PK_ASN1_ERROR;
                return CRYPT_PK_ASN1_ERROR;
             }
             }
             break;
             break;
@@ -130,51 +123,76 @@ int rsa_decode_parameters(const ltc_asn1_list *parameters, ltc_rsa_parameters *r
       }
       }
    }
    }
 
 
-
    rsa_params->pss_oaep = 1;
    rsa_params->pss_oaep = 1;
 
 
    return CRYPT_OK;
    return CRYPT_OK;
 }
 }
 
 
-static int s_rsa_import_pss(const unsigned char *in, unsigned long inlen, rsa_key *key)
+int rsa_decode_parameters(const ltc_asn1_list *parameters, ltc_rsa_parameters *rsa_params)
 {
 {
-   rsa_pss_parameters_data d;
-   ltc_asn1_list *decoded_list;
-   const ltc_asn1_list *spki;
    int           err;
    int           err;
+   rsa_pss_parameters_data d;
+
+   s_rsa_pss_parameters_data_setup(&d);
+
+   if ((err = der_decode_sequence(parameters->data, parameters->size, d.params, 4)) != CRYPT_OK) {
+      return err;
+   }
+
+   return s_rsa_decode_parameters(&d, rsa_params);
+}
+
+static LTC_INLINE int s_rsa_1_5_import_spki(const unsigned char *in, unsigned long inlen, rsa_key *key)
+{
+   return x509_process_public_key_from_spki(in, inlen,
+                                            LTC_OID_RSA,
+                                            LTC_ASN1_NULL, NULL, NULL,
+                                            (public_key_decode_cb)s_rsa_decode, key);
+}
+
+static LTC_INLINE int s_rsa_pss_import_spki(const unsigned char *in, unsigned long inlen, rsa_key *key)
+{
+   int err;
+   rsa_pss_parameters_data d;
    unsigned long n_params = LTC_ARRAY_SIZE(d.params);
    unsigned long n_params = LTC_ARRAY_SIZE(d.params);
 
 
+   if (x509_process_public_key_from_spki(in, inlen,
+                                         LTC_OID_RSA_PSS,
+                                         LTC_ASN1_NULL, NULL, NULL,
+                                         (public_key_decode_cb)s_rsa_decode, key) == CRYPT_OK) {
+      return CRYPT_OK;
+   }
+   s_rsa_pss_parameters_data_setup(&d);
    if ((err = x509_process_public_key_from_spki(in, inlen,
    if ((err = x509_process_public_key_from_spki(in, inlen,
-                                                LTC_OID_RSA_PSS,
-                                                LTC_ASN1_NULL, NULL, NULL,
-                                                (public_key_decode_cb)s_rsa_decode, key)) != CRYPT_OK) {
-      if ((err = x509_decode_spki(in, inlen, &decoded_list, &spki)) != CRYPT_OK) {
-         return err;
-      }
-      if ((err = x509_process_public_key_from_spki(spki->data, spki->size,
-                                              LTC_OID_RSA_PSS,
-                                              LTC_ASN1_NULL, NULL, NULL,
-                                              (public_key_decode_cb)s_rsa_decode, key)) != CRYPT_OK) {
-         s_rsa_pss_parameters_data_setup(&d);
-         err = x509_process_public_key_from_spki(spki->data, spki->size,
-                                                 LTC_OID_RSA_PSS,
-                                                 LTC_ASN1_SEQUENCE, d.params, &n_params,
-                                                 (public_key_decode_cb)s_rsa_decode, key);
-      }
+                                            LTC_OID_RSA_PSS,
+                                            LTC_ASN1_SEQUENCE, d.params, &n_params,
+                                            (public_key_decode_cb)s_rsa_decode, key)) != CRYPT_OK) {
+      return err;
+   }
+   return s_rsa_decode_parameters(&d, &key->params);
+}
+
+static LTC_INLINE int s_rsa_import_spki(const unsigned char *in, unsigned long inlen, rsa_key *key)
+{
+   int err;
+   if (s_rsa_1_5_import_spki(in, inlen, key) == CRYPT_OK) {
+      return CRYPT_OK;
    }
    }
 
 
-   der_free_sequence_flexi(decoded_list);
+   if ((err = s_rsa_pss_import_spki(in, inlen, key)) == CRYPT_OK) {
+      return CRYPT_OK;
+   }
    return err;
    return err;
 }
 }
 
 
 /**
 /**
-  Import an RSA key from a X.509 certificate
+  Import an RSA key from SubjectPublicKeyInfo
   @param in      The packet to import from
   @param in      The packet to import from
   @param inlen   It's length (octets)
   @param inlen   It's length (octets)
   @param key     [out] Destination for newly imported key
   @param key     [out] Destination for newly imported key
   @return CRYPT_OK if successful, upon error allocated memory is freed
   @return CRYPT_OK if successful, upon error allocated memory is freed
 */
 */
-int rsa_import_x509(const unsigned char *in, unsigned long inlen, rsa_key *key)
+int rsa_import_spki(const unsigned char *in, unsigned long inlen, rsa_key *key)
 {
 {
    int           err;
    int           err;
 
 
@@ -187,22 +205,58 @@ int rsa_import_x509(const unsigned char *in, unsigned long inlen, rsa_key *key)
       return err;
       return err;
    }
    }
 
 
-   if ((err = x509_decode_public_key_from_certificate(in, inlen,
-                                                      LTC_OID_RSA,
-                                                      LTC_ASN1_NULL, NULL, NULL,
-                                                      (public_key_decode_cb)s_rsa_decode, key)) == CRYPT_OK) {
+   if ((err = s_rsa_import_spki(in, inlen, key)) == CRYPT_OK) {
       key->type = PK_PUBLIC;
       key->type = PK_PUBLIC;
       return CRYPT_OK;
       return CRYPT_OK;
    }
    }
 
 
-   if ((err = s_rsa_import_pss(in, inlen, key)) == CRYPT_OK) {
+   rsa_free(key);
+
+   return err;
+}
+
+/**
+  Import an RSA key from a X.509 certificate
+  @param in      The packet to import from
+  @param inlen   It's length (octets)
+  @param key     [out] Destination for newly imported key
+  @return CRYPT_OK if successful, upon error allocated memory is freed
+*/
+int rsa_import_x509(const unsigned char *in, unsigned long inlen, rsa_key *key)
+{
+   ltc_asn1_list *decoded_list;
+   const ltc_asn1_list *spki;
+   int           err;
+
+   LTC_ARGCHK(in          != NULL);
+   LTC_ARGCHK(key         != NULL);
+   LTC_ARGCHK(ltc_mp.name != NULL);
+
+   /* init key */
+   if ((err = rsa_init(key)) != CRYPT_OK) {
+      return err;
+   }
+
+   /* First try to decode as SubjectPublicKeyInfo */
+   if (s_rsa_import_spki(in, inlen, key) == CRYPT_OK) {
       key->type = PK_PUBLIC;
       key->type = PK_PUBLIC;
       return CRYPT_OK;
       return CRYPT_OK;
    }
    }
 
 
-   rsa_free(key);
+   /* Now try to extract the SubjectPublicKeyInfo from the Certificate */
+   if ((err = x509_decode_spki(in, inlen, &decoded_list, &spki)) != CRYPT_OK) {
+      rsa_free(key);
+      return err;
+   }
+   err = s_rsa_import_spki(spki->data, spki->size, key);
 
 
-   return err;
+   der_free_sequence_flexi(decoded_list);
+   if (err != CRYPT_OK) {
+      rsa_free(key);
+      return err;
+   }
+   key->type = PK_PUBLIC;
+   return CRYPT_OK;
 }
 }
 
 
 #endif /* LTC_MRSA */
 #endif /* LTC_MRSA */