Add X.509 APIs

Signed-off-by: Steffen Jaeckel <s@jaeckel.eu>
This commit is contained in:
Steffen Jaeckel 2025-06-16 14:32:57 +02:00 committed by Karel Miko
parent 09b3817dc0
commit 55201c316f
11 changed files with 1978 additions and 77 deletions

View file

@ -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

View file

@ -1053,4 +1053,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

View file

@ -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
};
@ -444,13 +471,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);
#ifdef LTC_DER
int pk_get_oid_from_asn1(const ltc_asn1_list *oid, enum ltc_oid_id *id);
#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);
#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_cmp_with_ulong(const char *o1, const unsigned long *o2, unsigned long o2size);
/* ---- RSA Routines ---- */
@ -715,6 +743,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);
@ -733,6 +762,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);
@ -804,9 +839,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 */

View file

@ -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

View file

@ -8,55 +8,15 @@
*/
#ifdef LTC_DER
/**
Decode OID data and store the array of integers in words
@param in The OID DER encoded data
@param inlen The length of the OID data
@param words [out] The destination of the OID words
@param outlen [in/out] The number of OID words
@return CRYPT_OK if successful
*/
int der_decode_object_identifier(const unsigned char *in, unsigned long inlen,
unsigned long *words, unsigned long *outlen)
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;
LTC_ARGCHK(in != NULL);
LTC_ARGCHK(words != NULL);
LTC_ARGCHK(outlen != NULL);
/* header is at least 3 bytes */
if (inlen < 3) {
return CRYPT_INVALID_PACKET;
}
/* must be room for at least two words */
if (*outlen < 2) {
*outlen = 2;
return CRYPT_BUFFER_OVERFLOW;
}
/* decode the packet header */
x = 0;
if ((in[x++] & 0x1F) != 0x06) {
return CRYPT_INVALID_PACKET;
}
/* get the length of the data */
y = inlen - x;
if ((err = der_decode_asn1_length(in + x, &y, &len)) != CRYPT_OK) {
return err;
}
x += y;
if ((len == 0) || (len > (inlen - x))) {
return CRYPT_INVALID_PACKET;
}
/* decode words */
y = 0;
t = 0;
x = y = t = 0;
len = inlen;
while (len--) {
t = (t << 7) | (in[x] & 0x7F);
if (!(in[x++] & 0x80)) {
@ -91,4 +51,53 @@ int der_decode_object_identifier(const unsigned char *in, unsigned long inle
return err;
}
/**
Decode OID data and store the array of integers in words
@param in The OID DER encoded data
@param inlen The length of the OID data
@param words [out] The destination of the OID words
@param outlen [in/out] The number of OID words
@return CRYPT_OK if successful
*/
int der_decode_object_identifier(const unsigned char *in, unsigned long inlen,
unsigned long *words, unsigned long *outlen)
{
unsigned long x, y, len;
int err;
LTC_ARGCHK(in != NULL);
LTC_ARGCHK(words != NULL);
LTC_ARGCHK(outlen != NULL);
/* header is at least 3 bytes */
if (inlen < 3) {
return CRYPT_INVALID_PACKET;
}
/* must be room for at least two words */
if (*outlen < 2) {
*outlen = 2;
return CRYPT_BUFFER_OVERFLOW;
}
/* decode the packet header */
x = 0;
if ((in[x++] & 0x1F) != 0x06) {
return CRYPT_INVALID_PACKET;
}
/* get the length of the data */
y = inlen - x;
if ((err = der_decode_asn1_length(in + x, &y, &len)) != CRYPT_OK) {
return err;
}
x += y;
if ((len == 0) || (len > (inlen - x))) {
return CRYPT_INVALID_PACKET;
}
return der_decode_object_identifier_data(in + x, len, words, outlen);
}
#endif

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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 */

View file

@ -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 */

View file

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