/* LibTomCrypt, modular cryptographic library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
#include "tomcrypt_private.h"
/**
@file rsa_import.c
Import an RSA key from a X.509 certificate, Steffen Jaeckel
*/
#ifdef LTC_MRSA
#ifndef S_RSA_DECODE
#define S_RSA_DECODE
static int s_rsa_decode(const unsigned char *in, unsigned long inlen, rsa_key *key)
{
/* now it should be SEQUENCE { INTEGER, INTEGER } */
return der_decode_sequence_multi(in, inlen,
LTC_ASN1_INTEGER, 1UL, key->N,
LTC_ASN1_INTEGER, 1UL, key->e,
LTC_ASN1_EOL, 0UL, NULL);
}
#endif
typedef struct rsa_pss_parameters_data {
ltc_asn1_list params[4], inner[4], hash_alg[2], mgf[2], mgf_hash_alg[2];
unsigned long hash_alg_oid[LTC_DER_OID_DEFAULT_NODES];
unsigned long mgf_alg_oid[LTC_DER_OID_DEFAULT_NODES];
unsigned long mgf1_hash_alg_oid[LTC_DER_OID_DEFAULT_NODES];
unsigned long salt_length, trailer_field;
} rsa_pss_parameters_data;
static LTC_INLINE void s_rsa_pss_parameters_data_setup(rsa_pss_parameters_data *d)
{
unsigned long n;
/* RSASSA-PSS
*
* RSASSA-PSS-params ::= SEQUENCE {
* hashAlgorithm [0] HashAlgorithm DEFAULT sha1,
* maskGenAlgorithm [1] MaskGenAlgorithm DEFAULT mgf1SHA1,
* saltLength [2] INTEGER DEFAULT 20,
* trailerField [3] TrailerField DEFAULT trailerFieldBC
* }
*/
/* HashAlgorithm ::= AlgorithmIdentifier {
* {OAEP-PSSDigestAlgorithms}
* }
*/
LTC_SET_ASN1(d->hash_alg, 0, LTC_ASN1_OBJECT_IDENTIFIER, d->hash_alg_oid, LTC_ARRAY_SIZE(d->hash_alg_oid));
LTC_SET_ASN1(d->hash_alg, 1, LTC_ASN1_NULL, NULL, 0);
d->hash_alg[1].optional = 1;
/* MaskGenAlgorithm ::= AlgorithmIdentifier { {PKCS1MGFAlgorithms} } */
LTC_SET_ASN1(d->mgf_hash_alg, 0, LTC_ASN1_OBJECT_IDENTIFIER, d->mgf1_hash_alg_oid, LTC_ARRAY_SIZE(d->mgf1_hash_alg_oid));
LTC_SET_ASN1(d->mgf_hash_alg, 1, LTC_ASN1_NULL, NULL, 0);
d->mgf_hash_alg[1].optional = 1;
/* PKCS1MGFAlgorithms ALGORITHM-IDENTIFIER ::= {
* { OID id-mgf1 PARAMETERS HashAlgorithm },
* ... -- Allows for future expansion --
* }
*/
LTC_SET_ASN1(d->mgf, 0, LTC_ASN1_OBJECT_IDENTIFIER, d->mgf_alg_oid, LTC_ARRAY_SIZE(d->mgf_alg_oid));
LTC_SET_ASN1(d->mgf, 1, LTC_ASN1_SEQUENCE, d->mgf_hash_alg, LTC_ARRAY_SIZE(d->mgf_hash_alg));
LTC_SET_ASN1(d->inner, 0, LTC_ASN1_SEQUENCE, d->hash_alg, LTC_ARRAY_SIZE(d->hash_alg));
LTC_SET_ASN1(d->inner, 1, LTC_ASN1_SEQUENCE, d->mgf, LTC_ARRAY_SIZE(d->mgf));
LTC_SET_ASN1(d->inner, 2, LTC_ASN1_SHORT_INTEGER, &d->salt_length, 1UL);
LTC_SET_ASN1(d->inner, 3, LTC_ASN1_SHORT_INTEGER, &d->trailer_field, 1UL);
LTC_SET_ASN1_CUSTOM_CONSTRUCTED(d->params, 0, LTC_ASN1_CL_CONTEXT_SPECIFIC, 0, d->inner); /* context specific 0 */
LTC_SET_ASN1_CUSTOM_CONSTRUCTED(d->params, 1, LTC_ASN1_CL_CONTEXT_SPECIFIC, 1, d->inner + 1); /* context specific 1 */
LTC_SET_ASN1_CUSTOM_CONSTRUCTED(d->params, 2, LTC_ASN1_CL_CONTEXT_SPECIFIC, 2, d->inner + 2); /* context specific 2 */
LTC_SET_ASN1_CUSTOM_CONSTRUCTED(d->params, 3, LTC_ASN1_CL_CONTEXT_SPECIFIC, 3, d->inner + 3); /* context specific 3 */
for (n = 0; n < 4; ++n) {
d->params[n].optional = 1;
}
}
static int s_rsa_decode_parameters(const rsa_pss_parameters_data *d, ltc_rsa_parameters *rsa_params)
{
unsigned long n;
enum ltc_oid_id oid_id;
int err, idx;
rsa_params->saltlen = 20;
rsa_params->hash_idx = find_hash("sha1");
rsa_params->mgf1_hash_idx = rsa_params->hash_idx;
for (n = 0; n < 4; ++n) {
if (d->params[n].used == 0)
continue;
switch (n) {
case 0:
idx = find_hash_oid(d->hash_alg->data, d->hash_alg->size);
if (idx == -1) {
return CRYPT_INVALID_HASH;
}
rsa_params->hash_idx = idx;
break;
case 1:
if ((err = pk_get_oid_from_asn1(&d->mgf[0], &oid_id)) != CRYPT_OK) {
return err;
}
if (oid_id != LTC_OID_RSA_MGF1) {
return CRYPT_PK_ASN1_ERROR;
}
idx = find_hash_oid(d->mgf_hash_alg->data, d->mgf_hash_alg->size);
if (idx == -1) {
return CRYPT_INVALID_HASH;
}
rsa_params->mgf1_hash_idx = idx;
break;
case 2:
rsa_params->saltlen = d->salt_length;
break;
case 3:
if (d->trailer_field != 1) {
return CRYPT_PK_ASN1_ERROR;
}
break;
default:
return CRYPT_PK_ASN1_ERROR;
}
}
return CRYPT_OK;
}
int rsa_decode_parameters(const ltc_asn1_list *parameters, rsa_key *key)
{
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;
}
if ((err = s_rsa_decode_parameters(&d, &key->params)) != CRYPT_OK) {
return err;
}
key->pss_oaep = 1;
return CRYPT_OK;
}
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);
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,
LTC_OID_RSA_PSS,
LTC_ASN1_SEQUENCE, d.params, &n_params,
(public_key_decode_cb)s_rsa_decode, key)) != CRYPT_OK) {
return err;
}
if ((err = s_rsa_decode_parameters(&d, &key->params)) != CRYPT_OK) {
return err;
}
key->pss_oaep = 1;
return CRYPT_OK;
}
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;
}
if ((err = s_rsa_pss_import_spki(in, inlen, key)) == CRYPT_OK) {
return CRYPT_OK;
}
return err;
}
/**
Import an RSA key from SubjectPublicKeyInfo
@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_spki(const unsigned char *in, unsigned long inlen, rsa_key *key)
{
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;
}
if ((err = s_rsa_import_spki(in, inlen, key)) == CRYPT_OK) {
key->type = PK_PUBLIC;
return 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;
return CRYPT_OK;
}
/* 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);
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 */