mirror of
https://github.com/libtom/libtomcrypt
synced 2026-08-25 20:26:07 -04:00
Add X.509 APIs
Signed-off-by: Steffen Jaeckel <s@jaeckel.eu>
This commit is contained in:
parent
09b3817dc0
commit
55201c316f
11 changed files with 1978 additions and 77 deletions
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -23,6 +23,32 @@ static const oid_table_entry pka_oids[] = {
|
|||
{ LTC_OID_RSA_OAEP, LTC_PKA_RSA, NULL, "1.2.840.113549.1.1.7" },
|
||||
{ LTC_OID_RSA_MGF1, LTC_PKA_RSA, NULL, "1.2.840.113549.1.1.8" },
|
||||
{ LTC_OID_RSA_PSS, LTC_PKA_RSA_PSS, NULL, "1.2.840.113549.1.1.10" },
|
||||
{ LTC_OID_RSA_WITH_MD5, LTC_PKA_RSA, "md5", "1.2.840.113549.1.1.4" },
|
||||
{ LTC_OID_RSA_WITH_SHA1, LTC_PKA_RSA, "sha1", "1.2.840.113549.1.1.5" },
|
||||
{ LTC_OID_RSA_WITH_SHA224, LTC_PKA_RSA, "sha224", "1.2.840.113549.1.1.14" },
|
||||
{ LTC_OID_RSA_WITH_SHA256, LTC_PKA_RSA, "sha256", "1.2.840.113549.1.1.11" },
|
||||
{ LTC_OID_RSA_WITH_SHA384, LTC_PKA_RSA, "sha384", "1.2.840.113549.1.1.12" },
|
||||
{ LTC_OID_RSA_WITH_SHA512, LTC_PKA_RSA, "sha512", "1.2.840.113549.1.1.13" },
|
||||
{ LTC_OID_RSA_WITH_SHA512_224, LTC_PKA_RSA, "sha512-224", "1.2.840.113549.1.1.15" },
|
||||
{ LTC_OID_RSA_WITH_SHA512_512, LTC_PKA_RSA, "sha512-256", "1.2.840.113549.1.1.16" },
|
||||
{ LTC_OID_ECDSA_WITH_SHA1, LTC_PKA_EC, "sha1", "1.2.840.10045.4.1" },
|
||||
{ LTC_OID_ECDSA_WITH_SHA224, LTC_PKA_EC, "sha224", "1.2.840.10045.4.3.1" },
|
||||
{ LTC_OID_ECDSA_WITH_SHA256, LTC_PKA_EC, "sha256", "1.2.840.10045.4.3.2" },
|
||||
{ LTC_OID_ECDSA_WITH_SHA384, LTC_PKA_EC, "sha384", "1.2.840.10045.4.3.3" },
|
||||
{ LTC_OID_ECDSA_WITH_SHA512, LTC_PKA_EC, "sha512", "1.2.840.10045.4.3.4" },
|
||||
{ LTC_OID_DSA_WITH_SHA1, LTC_PKA_DSA, "sha1", "1.2.840.10040.4.3" },
|
||||
{ LTC_OID_DSA_WITH_SHA224, LTC_PKA_DSA, "sha224", "2.16.840.1.101.3.4.3.1" },
|
||||
{ LTC_OID_DSA_WITH_SHA256, LTC_PKA_DSA, "sha256", "2.16.840.1.101.3.4.3.2" },
|
||||
{ LTC_OID_DSA_WITH_SHA384, LTC_PKA_DSA, "sha384", "2.16.840.1.101.3.4.3.3" },
|
||||
{ LTC_OID_DSA_WITH_SHA512, LTC_PKA_DSA, "sha512", "2.16.840.1.101.3.4.3.4" },
|
||||
{ LTC_OID_ECDSA_WITH_SHA3_224, LTC_PKA_EC, "sha3-224", "2.16.840.1.101.3.4.3.9" },
|
||||
{ LTC_OID_ECDSA_WITH_SHA3_256, LTC_PKA_EC, "sha3-256", "2.16.840.1.101.3.4.3.10" },
|
||||
{ LTC_OID_ECDSA_WITH_SHA3_384, LTC_PKA_EC, "sha3-384", "2.16.840.1.101.3.4.3.11" },
|
||||
{ LTC_OID_ECDSA_WITH_SHA3_512, LTC_PKA_EC, "sha3-512", "2.16.840.1.101.3.4.3.12" },
|
||||
{ LTC_OID_RSA_WITH_SHA3_224, LTC_PKA_RSA, "sha3-512", "2.16.840.1.101.3.4.3.13" },
|
||||
{ LTC_OID_RSA_WITH_SHA3_256, LTC_PKA_RSA, "sha3-512", "2.16.840.1.101.3.4.3.14" },
|
||||
{ LTC_OID_RSA_WITH_SHA3_384, LTC_PKA_RSA, "sha3-512", "2.16.840.1.101.3.4.3.15" },
|
||||
{ LTC_OID_RSA_WITH_SHA3_512, LTC_PKA_RSA, "sha3-512", "2.16.840.1.101.3.4.3.16" },
|
||||
};
|
||||
|
||||
static LTC_INLINE const oid_table_entry* s_get_entry(enum ltc_oid_id id)
|
||||
|
|
@ -76,6 +102,16 @@ int pk_get_pka_id(enum ltc_oid_id id, enum ltc_pka_id *pka)
|
|||
return s_get_values(id, pka, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
Returns the Signature Algorithm requested, PKA ID + Hash algorithm.
|
||||
@return CRYPT_OK if valid
|
||||
*/
|
||||
int pk_get_sig_alg(enum ltc_oid_id id, ltc_x509_signature_algorithm *sig_alg)
|
||||
{
|
||||
LTC_ARGCHK(sig_alg != NULL);
|
||||
return s_get_values(id, &sig_alg->pka, &sig_alg->u.hash);
|
||||
}
|
||||
|
||||
/*
|
||||
Returns the OID ID requested.
|
||||
@return CRYPT_OK if valid
|
||||
|
|
|
|||
656
src/pk/asn1/x509/x509_extensions.c
Normal file
656
src/pk/asn1/x509/x509_extensions.c
Normal 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
|
||||
|
|
@ -4,16 +4,15 @@
|
|||
|
||||
/**
|
||||
@file x509_get_pka.c
|
||||
Extract the PKA from an X.509 cert, Steffen Jaeckel
|
||||
Extract details from an X.509 cert, Steffen Jaeckel
|
||||
*/
|
||||
|
||||
#ifdef LTC_DER
|
||||
|
||||
int x509_get_pka(const ltc_asn1_list *pub, enum ltc_pka_id *pka)
|
||||
static LTC_INLINE int s_x509_get_oid(const ltc_asn1_list *pub, enum ltc_oid_id *oid_id)
|
||||
{
|
||||
der_flexi_check flexi_should[4];
|
||||
der_flexi_check flexi_should[3];
|
||||
ltc_asn1_list *seqid, *id = NULL;
|
||||
enum ltc_oid_id oid_id;
|
||||
int err;
|
||||
unsigned long n = 0;
|
||||
LTC_SET_DER_FLEXI_CHECK(flexi_should, n++, LTC_ASN1_SEQUENCE, &seqid);
|
||||
|
|
@ -29,10 +28,27 @@ int x509_get_pka(const ltc_asn1_list *pub, enum ltc_pka_id *pka)
|
|||
if (err != CRYPT_OK && err != CRYPT_INPUT_TOO_LONG) {
|
||||
return err;
|
||||
}
|
||||
if ((err = pk_get_oid_from_asn1(id, &oid_id)) != CRYPT_OK) {
|
||||
return pk_get_oid_from_asn1(id, oid_id);
|
||||
}
|
||||
|
||||
int x509_get_pka(const ltc_asn1_list *pub, enum ltc_pka_id *pka)
|
||||
{
|
||||
int err;
|
||||
enum ltc_oid_id oid_id;
|
||||
if ((err = s_x509_get_oid(pub, &oid_id)) != CRYPT_OK) {
|
||||
return err;
|
||||
}
|
||||
return pk_get_pka_id(oid_id, pka);
|
||||
}
|
||||
|
||||
int x509_get_sig_alg(const ltc_asn1_list *seq, ltc_x509_signature_algorithm *sig_alg)
|
||||
{
|
||||
int err;
|
||||
enum ltc_oid_id oid_id;
|
||||
if ((err = s_x509_get_oid(seq, &oid_id)) != CRYPT_OK) {
|
||||
return err;
|
||||
}
|
||||
return pk_get_sig_alg(oid_id, sig_alg);
|
||||
}
|
||||
|
||||
#endif /* LTC_DER */
|
||||
683
src/pk/asn1/x509/x509_import.c
Normal file
683
src/pk/asn1/x509/x509_import.c
Normal 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 */
|
||||
194
src/pk/asn1/x509/x509_utils.c
Normal file
194
src/pk/asn1/x509/x509_utils.c
Normal 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";
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue