Add X.509 CRL support
Subsystems (4)
cover Add X.509 CRL support
This series adds X.509 Certificate Revocation List (CRL) parsing with indirect CRL support per RFC 5280. Revoked certificates are blacklisted using SHA-256 hash of serial+issuer.
The ASN.1 compiler is fixed to handle OPTIONAL fields in SEQUENCE/SET OF and TYPE_REF constructs, which is required for the new CRL definitions.
Timofei Novikov (3):
ASN.1: fix OPTIONAL for SEQUENCE OF and TYPE_REF
x509: add CRL parser with indirect CRL support
x509: add CRL signature verification support
certs/blacklist.c | 17 ++- crypto/asymmetric_keys/Makefile | 8 +- crypto/asymmetric_keys/x509_cert_parser.c | 132 ++++++++++++++++++++++ crypto/asymmetric_keys/x509_crl.asn1 | 59 ++++++++++ crypto/asymmetric_keys/x509_idp.asn1 | 12 ++ crypto/asymmetric_keys/x509_loader.c | 113 ++++++++++++++++++ crypto/asymmetric_keys/x509_parser.h | 18 +++ crypto/asymmetric_keys/x509_public_key.c | 21 ++++ include/keys/asymmetric-type.h | 4 + include/keys/system_keyring.h | 2 + include/linux/oid_registry.h | 4 + scripts/asn1_compiler.c | 12 +- 12 files changed, 395 insertions(+), 7 deletions(-) create mode 100644 crypto/asymmetric_keys/x509_crl.asn1 create mode 100644 crypto/asymmetric_keys/x509_idp.asn1
Signature · 1 line
2.43.0
PATCH 1/3 ASN.1: fix OPTIONAL for SEQUENCE OF and TYPE_REF
Fix two issues with OPTIONAL fields:
1. For TYPE_REF fields that are OPTIONAL, propagate the ELEMENT_SKIPPABLE flag to the target type via a temporary tag element. This ensures the target composite type generates MATCH_JUMP_OR_SKIP instead of MATCH.
2. OPTIONAL SEQUENCE/SET OF: set ELEMENT_SKIPPABLE on sequence wrapper, not just on parent field. This allows the entire sequence to be absent from the data.
--- scripts/asn1_compiler.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-)
scripts/asn1_compiler.c +11 -1
@@ -1039,6 +1039,8 @@ static struct element *parse_type(struct token **_cursor, struct token *end,if (cursor >= end) goto overrun_error; element->children =parse_type (&cursor, end,NULL ); + element->flags |= element->children->flags &ELEMENT_SKIPPABLE ; + element->children->flags &= ~ELEMENT_SKIPPABLE ; }else { element->children =parse_compound (&cursor, end,0 ); }
@@ -1056,6 +1058,8 @@ static struct element *parse_type(struct token **_cursor, struct token *end,if (cursor >= end) goto parse_error; element->children =parse_type (&cursor, end,NULL ); + element->flags |= element->children->flags &ELEMENT_SKIPPABLE ; + element->children->flags &= ~ELEMENT_SKIPPABLE ; }else { element->children =parse_compound (&cursor, end,1 ); }
@@ -1522,7 +1526,13 @@ static void render_element(FILE *out, struct element *e, struct element *tag)/* Deal with compound types */ switch (e->compound) {case TYPE_REF : -render_element (out, e->type->type->element, tag); +if (skippable && !tag) { +struct element tmp = { .flags =ELEMENT_SKIPPABLE }; + +render_element (out, e->type->type->element, &tmp); + }else { +render_element (out, e->type->type->element, tag); + }if (e->action)render_opcode (out,"ASN1_OP_%sACT,\n" , skippable ?"MAYBE_" :"" );
Signature · 1 line
2.43.0
PATCH 2/3 x509: add CRL parser with indirect CRL support
In reply to Timofei NovikovImplement X.509 Certificate Revocation List (CRL) parsing according to RFC 5280. The parser extracts the issuer and list of revoked certificates, then blacklists them using a hash of the certificate's serial number and issuer name.
Support indirect CRLs by parsing the Issuing Distribution Point (IDP) extension to detect the indirectCRL flag, and the Certificate Issuer extension on CRL entries to obtain the per-entry issuer. For indirect CRLs, the entry-specific issuer is used for blacklisting; for regular CRLs, the CRL issuer is used.
Add new OIDs for issuingDistributionPoint (2.5.29.28), certificateIssuer (2.5.29.29), and cRLReason (2.5.29.21).
The CRL is loaded via the blacklist keyring with a "crl:" prefix, similar to "tbs:" and "bin:" prefixes for other blacklist types.
--- certs/blacklist.c | 17 ++- crypto/asymmetric_keys/Makefile | 8 +- crypto/asymmetric_keys/x509_cert_parser.c | 121 ++++++++++++++++++++++ crypto/asymmetric_keys/x509_crl.asn1 | 59 +++++++++++ crypto/asymmetric_keys/x509_idp.asn1 | 12 +++ crypto/asymmetric_keys/x509_loader.c | 84 +++++++++++++++ crypto/asymmetric_keys/x509_parser.h | 18 ++++ crypto/asymmetric_keys/x509_public_key.c | 21 ++++ include/keys/asymmetric-type.h | 3 + include/keys/system_keyring.h | 2 + include/linux/oid_registry.h | 4 + 11 files changed, 343 insertions(+), 6 deletions(-) create mode 100644 crypto/asymmetric_keys/x509_crl.asn1 create mode 100644 crypto/asymmetric_keys/x509_idp.asn1
certs/blacklist.c +12 -5
@@ -32,6 +32,7 @@static const char tbs_prefix[] ="tbs" ;static const char bin_prefix[] ="bin" ; +static const char crl_prefix[] ="crl" ;static struct key *blacklist_keyring;
@@ -46,16 +47,16 @@ extern __initconst const unsigned long revocation_certificate_list_size;*/ static int blacklist_vet_description (const char *desc) { -int i, prefix_len, tbs_step =0 , bin_step =0 ; +int i, prefix_len, tbs_step =0 , bin_step =0 , crl_step =0 ;/* The following algorithm only works if prefix lengths match. */ -BUILD_BUG_ON (sizeof (tbs_prefix) !=sizeof (bin_prefix)); +BUILD_BUG_ON (sizeof (tbs_prefix) !=sizeof (bin_prefix) || +sizeof (tbs_prefix) !=sizeof (crl_prefix)); prefix_len =sizeof (tbs_prefix) -1 ;for (i =0 ; *desc; desc++, i++) {if (*desc ==':' ) { -if (tbs_step == prefix_len) - goto found_colon; -if (bin_step == prefix_len) +if (tbs_step == prefix_len || bin_step == prefix_len || + crl_step == prefix_len) goto found_colon;return -EINVAL ; }
@@ -65,6 +66,8 @@ static int blacklist_vet_description(const char *desc) tbs_step++;if (*desc == bin_prefix[i]) bin_step++; +if (*desc == crl_prefix[i]) + crl_step++; }return -EINVAL ;
@@ -160,6 +163,10 @@ static char *get_raw_hash(const u8 *hash, size_t hash_len, type_len =sizeof (bin_prefix) -1 ; type_prefix = bin_prefix;break ; +case BLACKLIST_HASH_X509_CRL : + type_len =sizeof (crl_prefix) -1 ; + type_prefix = crl_prefix; +break ;default :WARN_ON_ONCE (1 );return ERR_PTR (-EINVAL );
crypto/asymmetric_keys/Makefile +7 -1
@@ -19,6 +19,8 @@ obj-$(CONFIG_X509_CERTIFICATE_PARSER) += x509_key_parser.o x509_key_parser-y := \ x509.asn1.o \ x509_akid.asn1.o \ + x509_crl.asn1.o \ + x509_idp.asn1.o \ x509_cert_parser.o \ x509_loader.o \ x509_public_key.o
@@ -29,10 +31,14 @@ x509_selftest-$(CONFIG_FIPS_SIGNATURE_SELFTEST_ECDSA) += selftest_ecdsa.o $(obj)/x509_cert_parser.o: \ $(obj)/x509.asn1.h \ - $(obj)/x509_akid.asn1.h + $(obj)/x509_akid.asn1.h \ + $(obj)/x509_crl.asn1.h \ + $(obj)/x509_idp.asn1.h $(obj)/x509.asn1.o: $(obj)/x509.asn1.c $(obj)/x509.asn1.h $(obj)/x509_akid.asn1.o: $(obj)/x509_akid.asn1.c $(obj)/x509_akid.asn1.h +$(obj)/x509_crl.asn1.o: $(obj)/x509_crl.asn1.c $(obj)/x509_crl.asn1.h +$(obj)/x509_idp.asn1.o: $(obj)/x509_idp.asn1.c $(obj)/x509_idp.asn1.h # # PKCS#8 private key handling
crypto/asymmetric_keys/x509_cert_parser.c +121 -0
148 lines not on this page · raw message
crypto/asymmetric_keys/x509_crl.asn1 new file +59 -0
59 lines not on this page · raw message
crypto/asymmetric_keys/x509_idp.asn1 new file +12 -0
@@ -0,0 +1,12 @@ +-- +-- Issuing Distribution Point (RFC 5280, section 5.2.5) +-- + +IssuingDistributionPoint ::= SEQUENCE { + distributionPoint [0] ANY OPTIONAL, + onlyContainsUserCerts [1] IMPLICIT BOOLEAN OPTIONAL, + onlyContainsCACerts [2] IMPLICIT BOOLEAN OPTIONAL, + onlySomeReasons [3] IMPLICIT BIT STRING OPTIONAL, + indirectCRL [4] IMPLICIT BOOLEAN OPTIONAL ({ x509_note_indirect_crl }), + onlyContainsAttributeCerts [5] IMPLICIT BOOLEAN OPTIONAL + }
crypto/asymmetric_keys/x509_loader.c +84 -0
93 lines not on this page · raw message
crypto/asymmetric_keys/x509_parser.h +18 -0
@@ -46,6 +46,22 @@ struct x509_certificate {/* * x509_cert_parser.c */ +struct x509_crl_context { +const void *raw_issuer; +size_t raw_issuer_size; +struct list_head revoked_list; +bool indirect_crl; +u8 crl_reason; +}; + +struct x509_revoked_entry { +struct list_head list; +u8 *serial; +size_t serial_size; +const void *raw_issuer;/* for indirect */ +size_t raw_issuer_size; +}; +extern void x509_free_certificate (struct x509_certificate *cert); DEFINE_FREE(x509_free_certificate, struct x509_certificate *, if (!IS_ERR(_T)) x509_free_certificate(_T))
@@ -53,6 +69,8 @@ extern struct x509_certificate *x509_cert_parse(const void *data, size_t datalenextern int x509_decode_time (time64_t *_t,size_t hdrlen,unsigned char tag,const unsigned char *value,size_t vlen); +struct x509_crl_context *x509_crl_parse (const void *data,size_t datalen); +void x509_crl_free (struct x509_crl_context *ctx);/* * x509_public_key.c
crypto/asymmetric_keys/x509_public_key.c +21 -0
@@ -15,6 +15,7 @@#include <linux/kernel.h> #include <linux/slab.h> #include <linux/string.h> +#include <crypto/sha2.h> #include "asymmetric_keys.h" #include "x509_parser.h"
@@ -90,6 +91,26 @@ int x509_get_sig_params(struct x509_certificate *cert)if (ret <0 ) goto error_2; +/* Check CRL blacklist (hash of serial + issuer) */ +if (!cert->blacklisted && cert->raw_serial && cert->raw_issuer) { +struct asymmetric_key_id *kid; + + kid =asymmetric_key_generate_id (cert->raw_serial, cert->raw_serial_size, + cert->raw_issuer, cert->raw_issuer_size); +if (!IS_ERR (kid)) { +u8 digest[SHA256_DIGEST_SIZE ]; + +sha256 (kid->data, kid->len, digest); + ret =is_hash_blacklisted (digest,SHA256_DIGEST_SIZE , +BLACKLIST_HASH_X509_CRL ); +if (ret == -EKEYREJECTED ) { +pr_err ("Cert is CRL-blacklisted\n" ); + cert->blacklisted = true; + } +kfree (kid); + } + } + error_2:kfree (desc); error:
include/keys/asymmetric-type.h +3 -0
@@ -86,6 +86,9 @@ extern struct key *find_asymmetric_key(struct key *keyring,int x509_load_certificate_list (const u8 cert_list[],const unsigned long list_size,const struct key *keyring); +#ifdef CONFIG_SYSTEM_BLACKLIST_KEYRING +int x509_load_crl_list (const u8 crl_list[],const unsigned long list_size); +#endif /* * The payload is at the discretion of the subtype.
include/keys/system_keyring.h +2 -0
@@ -15,6 +15,8 @@ enum blacklist_hash_type {BLACKLIST_HASH_X509_TBS =1 ,/* Raw data hash */ BLACKLIST_HASH_BINARY =2 , +/* Hash = serial + issuer */ + BLACKLIST_HASH_X509_CRL = 3, };#ifdef CONFIG_SYSTEM_TRUSTED_KEYRING
include/linux/oid_registry.h +4 -0
@@ -150,6 +150,10 @@ enum OID { OID_id_ml_dsa_65,/* 2.16.840.1.101.3.4.3.18 */ OID_id_ml_dsa_87,/* 2.16.840.1.101.3.4.3.19 */ + OID_id_ce_issuingDistributionPoint,/* 2.5.29.28 */ + OID_id_ce_certificateIssuer,/* 2.5.29.29 */ + OID_id_ce_cRLReason,/* 2.5.29.21 */ + OID__NR };
Signature · 1 line
2.43.0
PATCH 3/3 x509: add CRL signature verification support
In reply to Timofei NovikovVerify the digital signature of Certificate Revocation Lists (CRLs) before processing their entries. Extract signature parameters via x509_get_sig_params() and preserve the TBS data during parsing to enable signature verification with verify_signature().
Add the keyring parameter to x509_load_crl_list() to specify which keyring to use for CRL issuer lookup. If no keyring is provided, signature verification is skipped.
--- crypto/asymmetric_keys/x509_cert_parser.c | 11 ++++++++ crypto/asymmetric_keys/x509_crl.asn1 | 2 +- crypto/asymmetric_keys/x509_loader.c | 31 ++++++++++++++++++++++- crypto/asymmetric_keys/x509_parser.h | 2 +- include/keys/asymmetric-type.h | 3 ++- 5 files changed, 45 insertions(+), 4 deletions(-)
crypto/asymmetric_keys/x509_cert_parser.c +11 -0
@@ -907,14 +907,24 @@ struct x509_crl_context *x509_crl_parse(const void *data, size_t datalen)if (ret <0 )return ERR_PTR (ret); +/* +* Extract signature parameters (sig->s, sig->digest, etc.) +* This does what we previously tried to do manually. +*/ + ret =x509_get_sig_params (cert); +if (ret <0 ) +return ERR_PTR (ret); +/* Transfer parsed data to crl_context */ INIT_LIST_HEAD (&crl_ctx->revoked_list); crl_ctx->indirect_crl = ctx->indirect_crl; crl_ctx->raw_issuer = cert->raw_issuer; crl_ctx->raw_issuer_size = cert->raw_issuer_size; + crl_ctx->sig = cert->sig;list_splice (&ctx->revoked_list, &crl_ctx->revoked_list);/* Detach pointers from cert before auto-free */ + cert->sig =NULL ; cert->raw_issuer =NULL ; cert->raw_serial =NULL ;
@@ -930,6 +940,7 @@ void x509_crl_free(struct x509_crl_context *ctx)kfree (entry->serial);kfree (entry); } +public_key_signature_free (ctx->sig);kfree (ctx); }
crypto/asymmetric_keys/x509_crl.asn1 +1 -1
@@ -16,7 +16,7 @@ TBSCertList ::= SEQUENCE { nextUpdate Time OPTIONAL ({ x509_note_not_after }) , revokedCertificates SEQUENCE OF RevokedCertificate OPTIONAL, crlExtensions [ 0 ] Extensions OPTIONAL - } + } ({ x509_note_tbs_certificate }) RevokedCertificate ::= SEQUENCE { userCertificate CertificateSerialNumber ({ crl_note_serial }),
crypto/asymmetric_keys/x509_loader.c +30 -1
@@ -6,6 +6,7 @@#include <keys/system_keyring.h> #include <linux/slab.h> #include <crypto/sha2.h> +#include <crypto/public_key.h> #include "x509_parser.h" int x509_load_certificate_list(const u8 cert_list[],
@@ -74,7 +75,30 @@ static const void *crl_entry_issuer(const struct x509_crl_context *crl,return crl->raw_issuer; } -int x509_load_crl_list (const u8 crl_list[],const unsigned long list_size) +static int x509_crl_verify_signature (struct x509_crl_context *crl_ctx, +const struct key *keyring) +{ +struct key *key; +int ret = -ENOKEY ; + +if (!crl_ctx->raw_issuer || !crl_ctx->sig) +return -ENOKEY ; + + key =find_asymmetric_key (keyring, + crl_ctx->sig->auth_ids[0 ], + crl_ctx->sig->auth_ids[1 ], + crl_ctx->sig->auth_ids[2 ], + false); +if (IS_ERR (key)) +return PTR_ERR (key); + + ret =verify_signature (key, crl_ctx->sig); +key_put (key); +return ret; +} + +int x509_load_crl_list (const u8 crl_list[],const unsigned long list_size, +const struct key *keyring) {const u8 *p = crl_list, *end = p + list_size;
@@ -107,6 +131,11 @@ int x509_load_crl_list(const u8 crl_list[], const unsigned long list_size)pr_err ("Problem parsing CRL (%ld)\n" ,PTR_ERR (crl_ctx)); goto next_crl; } +if (keyring &&x509_crl_verify_signature (crl_ctx, keyring) <0 ) { +pr_warn ("CRL signature verification failed, skipping\n" ); +x509_crl_free (crl_ctx); + goto next_crl; + }list_for_each_entry_safe (entry, tmp, &crl_ctx->revoked_list, list) {struct asymmetric_key_id *kid;const void *issuer;
crypto/asymmetric_keys/x509_parser.h +1 -1
@@ -49,9 +49,9 @@ struct x509_certificate {struct x509_crl_context {const void *raw_issuer;size_t raw_issuer_size; +struct public_key_signature *sig;struct list_head revoked_list;bool indirect_crl; -u8 crl_reason; };struct x509_revoked_entry {
include/keys/asymmetric-type.h +2 -1
@@ -87,7 +87,8 @@ extern struct key *find_asymmetric_key(struct key *keyring,int x509_load_certificate_list (const u8 cert_list[],const unsigned long list_size,const struct key *keyring);#ifdef CONFIG_SYSTEM_BLACKLIST_KEYRING -int x509_load_crl_list (const u8 crl_list[],const unsigned long list_size); +int x509_load_crl_list (const u8 crl_list[],const unsigned long list_size, +const struct key *keyring);#endif /*
Signature · 1 line
2.43.0