// SPDX-License-Identifier: GPL-2.0+ /* * X509 cert parser using MbedTLS X509 library * * Copyright (c) 2024 Linaro Limited * Author: Raymond Mao */ #include #include #include static void x509_free_mbedtls_ctx(struct x509_cert_mbedtls_ctx *ctx) { if (!ctx) return; kfree(ctx->tbs); kfree(ctx->raw_serial); kfree(ctx->raw_issuer); kfree(ctx->raw_subject); kfree(ctx->raw_skid); kfree(ctx); } static int x509_set_cert_flags(struct x509_certificate *cert) { struct public_key_signature *sig = cert->sig; if (!sig || !cert->pub) { pr_err("Signature or public key is not initialized\n"); return -ENOPKG; } if (!cert->pub->pkey_algo) cert->unsupported_key = true; if (!sig->pkey_algo) cert->unsupported_sig = true; if (!sig->hash_algo) cert->unsupported_sig = true; /* TODO: is_hash_blacklisted()? */ /* Detect self-signed certificates and set self_signed flag */ return x509_check_for_self_signed(cert); } time64_t x509_get_timestamp(const mbedtls_x509_time *x509_time) { unsigned int year, mon, day, hour, min, sec; /* Adjust for year since 1900 */ year = x509_time->year - 1900; /* Adjust for 0-based month */ mon = x509_time->mon - 1; day = x509_time->day; hour = x509_time->hour; min = x509_time->min; sec = x509_time->sec; return (time64_t)mktime64(year, mon, day, hour, min, sec); } static char *x509_populate_dn_name_string(const mbedtls_x509_name *name) { size_t len = 256; int wb; char *name_str; do { name_str = kzalloc(len, GFP_KERNEL); if (!name_str) return NULL; wb = mbedtls_x509_dn_gets(name_str, len, name); if (wb < 0) { pr_err("Get DN string failed, ret:-0x%04x\n", (unsigned int)-wb); kfree(name_str); len = len * 2; /* Try with a bigger buffer */ } } while (wb < 0); name_str[wb] = '\0'; /* add the terminator */ return name_str; } static int x509_populate_signature_params(const mbedtls_x509_crt *cert, struct public_key_signature **sig) { struct public_key_signature *s; struct image_region region; size_t akid_len; unsigned char *akid_data; int ret; /* Check if signed data exist */ if (!cert->tbs.p || !cert->tbs.len) return -EINVAL; region.data = cert->tbs.p; region.size = cert->tbs.len; s = kzalloc(sizeof(*s), GFP_KERNEL); if (!s) return -ENOMEM; /* * Get the public key algorithm. * Note: * ECRDSA (Elliptic Curve Russian Digital Signature Algorithm) is not * supported by MbedTLS. */ switch (cert->sig_pk) { case MBEDTLS_PK_RSA: s->pkey_algo = "rsa"; break; default: ret = -EINVAL; goto error_sig; } /* Get the hash algorithm */ switch (cert->sig_md) { case MBEDTLS_MD_SHA1: s->hash_algo = "sha1"; s->digest_size = SHA1_SUM_LEN; break; case MBEDTLS_MD_SHA256: s->hash_algo = "sha256"; s->digest_size = SHA256_SUM_LEN; break; case MBEDTLS_MD_SHA384: s->hash_algo = "sha384"; s->digest_size = SHA384_SUM_LEN; break; case MBEDTLS_MD_SHA512: s->hash_algo = "sha512"; s->digest_size = SHA512_SUM_LEN; break; /* Unsupported algo */ case MBEDTLS_MD_MD5: case MBEDTLS_MD_SHA224: default: ret = -EINVAL; goto error_sig; } /* * Optional attributes: * auth_ids holds AuthorityKeyIdentifier (information of issuer), * aka akid, which is used to match with a cert's id or skid to * indicate that is the issuer when we lookup a cert chain. * * auth_ids[0]: * [PKCS#7 or CMS ver 1] - generated from "Issuer + Serial number" * [CMS ver 3] - generated from skid (subjectKeyId) * auth_ids[1]: generated from skid (subjectKeyId) * * Assume that we are using PKCS#7 (msg->version=1), * not CMS ver 3 (msg->version=3). */ akid_len = cert->authority_key_id.authorityCertSerialNumber.len; akid_data = cert->authority_key_id.authorityCertSerialNumber.p; /* Check if serial number exists */ if (akid_len && akid_data) { s->auth_ids[0] = asymmetric_key_generate_id(akid_data, akid_len, cert->issuer_raw.p, cert->issuer_raw.len); if (!s->auth_ids[0]) { ret = -ENOMEM; goto error_sig; } } akid_len = cert->authority_key_id.keyIdentifier.len; akid_data = cert->authority_key_id.keyIdentifier.p; /* Check if subjectKeyId exists */ if (akid_len && akid_data) { s->auth_ids[1] = asymmetric_key_generate_id(akid_data, akid_len, "", 0); if (!s->auth_ids[1]) { ret = -ENOMEM; goto error_sig; } } /* * Encoding can be pkcs1 or raw, but only pkcs1 is supported. * Set the encoding explicitly to pkcs1. */ s->encoding = "pkcs1"; /* Copy the signature data */ s->s = kmemdup(cert->sig.p, cert->sig.len, GFP_KERNEL); if (!s->s) { ret = -ENOMEM; goto error_sig; } s->s_size = cert->sig.len; /* Calculate the digest of signed data (tbs) */ s->digest = kzalloc(s->digest_size, GFP_KERNEL); if (!s->digest) { ret = -ENOMEM; goto error_sig; } ret = hash_calculate(s->hash_algo, ®ion, 1, s->digest); if (!ret) *sig = s; return ret; error_sig: public_key_signature_free(s); return ret; } static int x509_save_mbedtls_ctx(const mbedtls_x509_crt *cert, struct x509_cert_mbedtls_ctx **pctx) { struct x509_cert_mbedtls_ctx *ctx; ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); if (!ctx) return -ENOMEM; /* Signed data (tbs - The part that is To Be Signed)*/ ctx->tbs = kmemdup(cert->tbs.p, cert->tbs.len, GFP_KERNEL); if (!ctx->tbs) goto error_ctx; /* Raw serial number */ ctx->raw_serial = kmemdup(cert->serial.p, cert->serial.len, GFP_KERNEL); if (!ctx->raw_serial) goto error_ctx; /* Raw issuer */ ctx->raw_issuer = kmemdup(cert->issuer_raw.p, cert->issuer_raw.len, GFP_KERNEL); if (!ctx->raw_issuer) goto error_ctx; /* Raw subject */ ctx->raw_subject = kmemdup(cert->subject_raw.p, cert->subject_raw.len, GFP_KERNEL); if (!ctx->raw_subject) goto error_ctx; /* Raw subjectKeyId */ ctx->raw_skid = kmemdup(cert->subject_key_id.p, cert->subject_key_id.len, GFP_KERNEL); if (!ctx->raw_skid) goto error_ctx; *pctx = ctx; return 0; error_ctx: x509_free_mbedtls_ctx(ctx); return -ENOMEM; } /* * Free an X.509 certificate */ void x509_free_certificate(struct x509_certificate *cert) { if (cert) { public_key_free(cert->pub); public_key_signature_free(cert->sig); kfree(cert->issuer); kfree(cert->subject); kfree(cert->id); kfree(cert->skid); x509_free_mbedtls_ctx(cert->mbedtls_ctx); kfree(cert); } } int x509_populate_pubkey(mbedtls_x509_crt *cert, struct public_key **pub_key) { struct public_key *pk; pk = kzalloc(sizeof(*pk), GFP_KERNEL); if (!pk) return -ENOMEM; pk->key = kzalloc(cert->pk_raw.len, GFP_KERNEL); if (!pk->key) { kfree(pk); return -ENOMEM; } memcpy(pk->key, cert->pk_raw.p, cert->pk_raw.len); pk->keylen = cert->pk_raw.len; /* * For ECC keys, params field might include information about the curve used, * the generator point, or other algorithm-specific parameters. * For RSA keys, it's common for the params field to be NULL. * FIXME: Assume that we just support RSA keys with id_type X509. */ pk->params = NULL; pk->paramlen = 0; pk->key_is_private = false; pk->id_type = "X509"; pk->pkey_algo = "rsa"; pk->algo = OID_rsaEncryption; *pub_key = pk; return 0; } int x509_populate_cert(mbedtls_x509_crt *mbedtls_cert, struct x509_certificate **pcert) { struct x509_certificate *cert; struct asymmetric_key_id *kid; struct asymmetric_key_id *skid; int ret; cert = kzalloc(sizeof(*cert), GFP_KERNEL); if (!cert) return -ENOMEM; /* Public key details */ ret = x509_populate_pubkey(mbedtls_cert, &cert->pub); if (ret) goto error_cert_pop; /* Signature parameters */ ret = x509_populate_signature_params(mbedtls_cert, &cert->sig); if (ret) goto error_cert_pop; ret = -ENOMEM; /* Name of certificate issuer */ cert->issuer = x509_populate_dn_name_string(&mbedtls_cert->issuer); if (!cert->issuer) goto error_cert_pop; /* Name of certificate subject */ cert->subject = x509_populate_dn_name_string(&mbedtls_cert->subject); if (!cert->subject) goto error_cert_pop; /* Certificate validity */ cert->valid_from = x509_get_timestamp(&mbedtls_cert->valid_from); cert->valid_to = x509_get_timestamp(&mbedtls_cert->valid_to); /* Save mbedtls context we need */ ret = x509_save_mbedtls_ctx(mbedtls_cert, &cert->mbedtls_ctx); if (ret) goto error_cert_pop; /* Signed data (tbs - The part that is To Be Signed)*/ cert->tbs = cert->mbedtls_ctx->tbs; cert->tbs_size = mbedtls_cert->tbs.len; /* Raw serial number */ cert->raw_serial = cert->mbedtls_ctx->raw_serial; cert->raw_serial_size = mbedtls_cert->serial.len; /* Raw issuer */ cert->raw_issuer = cert->mbedtls_ctx->raw_issuer; cert->raw_issuer_size = mbedtls_cert->issuer_raw.len; /* Raw subject */ cert->raw_subject = cert->mbedtls_ctx->raw_subject; cert->raw_subject_size = mbedtls_cert->subject_raw.len; /* Raw subjectKeyId */ cert->raw_skid = cert->mbedtls_ctx->raw_skid; cert->raw_skid_size = mbedtls_cert->subject_key_id.len; /* Generate cert issuer + serial number key ID */ kid = asymmetric_key_generate_id(cert->raw_serial, cert->raw_serial_size, cert->raw_issuer, cert->raw_issuer_size); if (IS_ERR(kid)) { ret = PTR_ERR(kid); goto error_cert_pop; } cert->id = kid; /* Generate subject + subjectKeyId */ skid = asymmetric_key_generate_id(cert->raw_skid, cert->raw_skid_size, "", 0); if (IS_ERR(skid)) { ret = PTR_ERR(skid); goto error_cert_pop; } cert->skid = skid; /* * Set the certificate flags: * self_signed, unsupported_key, unsupported_sig, blacklisted */ ret = x509_set_cert_flags(cert); if (!ret) { *pcert = cert; return 0; } error_cert_pop: x509_free_certificate(cert); return ret; } struct x509_certificate *x509_cert_parse(const void *data, size_t datalen) { mbedtls_x509_crt mbedtls_cert; struct x509_certificate *cert = NULL; long ret; /* Parse DER encoded certificate */ mbedtls_x509_crt_init(&mbedtls_cert); ret = mbedtls_x509_crt_parse_der(&mbedtls_cert, data, datalen); if (ret) goto clean_up_ctx; /* Populate x509_certificate from mbedtls_x509_crt */ ret = x509_populate_cert(&mbedtls_cert, &cert); if (ret) goto clean_up_ctx; clean_up_ctx: mbedtls_x509_crt_free(&mbedtls_cert); if (!ret) return cert; return ERR_PTR(ret); }