Raymond Mao | 7deec0f | 2024-10-03 14:50:30 -0700 | [diff] [blame] | 1 | // SPDX-License-Identifier: GPL-2.0+ |
| 2 | /* |
| 3 | * X509 cert parser using MbedTLS X509 library |
| 4 | * |
| 5 | * Copyright (c) 2024 Linaro Limited |
| 6 | * Author: Raymond Mao <raymond.mao@linaro.org> |
| 7 | */ |
| 8 | |
| 9 | #include <linux/err.h> |
| 10 | #include <crypto/public_key.h> |
| 11 | #include <crypto/x509_parser.h> |
| 12 | |
| 13 | static void x509_free_mbedtls_ctx(struct x509_cert_mbedtls_ctx *ctx) |
| 14 | { |
| 15 | if (!ctx) |
| 16 | return; |
| 17 | |
| 18 | kfree(ctx->tbs); |
| 19 | kfree(ctx->raw_serial); |
| 20 | kfree(ctx->raw_issuer); |
| 21 | kfree(ctx->raw_subject); |
| 22 | kfree(ctx->raw_skid); |
| 23 | kfree(ctx); |
| 24 | } |
| 25 | |
| 26 | static int x509_set_cert_flags(struct x509_certificate *cert) |
| 27 | { |
| 28 | struct public_key_signature *sig = cert->sig; |
| 29 | |
| 30 | if (!sig || !cert->pub) { |
| 31 | pr_err("Signature or public key is not initialized\n"); |
| 32 | return -ENOPKG; |
| 33 | } |
| 34 | |
| 35 | if (!cert->pub->pkey_algo) |
| 36 | cert->unsupported_key = true; |
| 37 | |
| 38 | if (!sig->pkey_algo) |
| 39 | cert->unsupported_sig = true; |
| 40 | |
| 41 | if (!sig->hash_algo) |
| 42 | cert->unsupported_sig = true; |
| 43 | |
| 44 | /* TODO: is_hash_blacklisted()? */ |
| 45 | |
| 46 | /* Detect self-signed certificates and set self_signed flag */ |
| 47 | return x509_check_for_self_signed(cert); |
| 48 | } |
| 49 | |
| 50 | time64_t x509_get_timestamp(const mbedtls_x509_time *x509_time) |
| 51 | { |
| 52 | unsigned int year, mon, day, hour, min, sec; |
| 53 | |
| 54 | /* Adjust for year since 1900 */ |
| 55 | year = x509_time->year - 1900; |
| 56 | /* Adjust for 0-based month */ |
| 57 | mon = x509_time->mon - 1; |
| 58 | day = x509_time->day; |
| 59 | hour = x509_time->hour; |
| 60 | min = x509_time->min; |
| 61 | sec = x509_time->sec; |
| 62 | |
| 63 | return (time64_t)mktime64(year, mon, day, hour, min, sec); |
| 64 | } |
| 65 | |
| 66 | static char *x509_populate_dn_name_string(const mbedtls_x509_name *name) |
| 67 | { |
| 68 | size_t len = 256; |
| 69 | size_t wb; |
| 70 | char *name_str; |
| 71 | |
| 72 | do { |
| 73 | name_str = kzalloc(len, GFP_KERNEL); |
| 74 | if (!name_str) |
| 75 | return NULL; |
| 76 | |
| 77 | wb = mbedtls_x509_dn_gets(name_str, len, name); |
| 78 | if (wb < 0) { |
| 79 | pr_err("Get DN string failed, ret:-0x%04x\n", |
| 80 | (unsigned int)-wb); |
| 81 | kfree(name_str); |
| 82 | len = len * 2; /* Try with a bigger buffer */ |
| 83 | } |
| 84 | } while (wb < 0); |
| 85 | |
| 86 | name_str[wb] = '\0'; /* add the terminator */ |
| 87 | |
| 88 | return name_str; |
| 89 | } |
| 90 | |
| 91 | static int x509_populate_signature_params(const mbedtls_x509_crt *cert, |
| 92 | struct public_key_signature **sig) |
| 93 | { |
| 94 | struct public_key_signature *s; |
| 95 | struct image_region region; |
| 96 | size_t akid_len; |
| 97 | unsigned char *akid_data; |
| 98 | int ret; |
| 99 | |
| 100 | /* Check if signed data exist */ |
| 101 | if (!cert->tbs.p || !cert->tbs.len) |
| 102 | return -EINVAL; |
| 103 | |
| 104 | region.data = cert->tbs.p; |
| 105 | region.size = cert->tbs.len; |
| 106 | |
| 107 | s = kzalloc(sizeof(*s), GFP_KERNEL); |
| 108 | if (!s) |
| 109 | return -ENOMEM; |
| 110 | |
| 111 | /* |
| 112 | * Get the public key algorithm. |
| 113 | * Note: |
| 114 | * ECRDSA (Elliptic Curve Russian Digital Signature Algorithm) is not |
| 115 | * supported by MbedTLS. |
| 116 | */ |
| 117 | switch (cert->sig_pk) { |
| 118 | case MBEDTLS_PK_RSA: |
| 119 | s->pkey_algo = "rsa"; |
| 120 | break; |
| 121 | default: |
| 122 | ret = -EINVAL; |
| 123 | goto error_sig; |
| 124 | } |
| 125 | |
| 126 | /* Get the hash algorithm */ |
| 127 | switch (cert->sig_md) { |
| 128 | case MBEDTLS_MD_SHA1: |
| 129 | s->hash_algo = "sha1"; |
| 130 | s->digest_size = SHA1_SUM_LEN; |
| 131 | break; |
| 132 | case MBEDTLS_MD_SHA256: |
| 133 | s->hash_algo = "sha256"; |
| 134 | s->digest_size = SHA256_SUM_LEN; |
| 135 | break; |
| 136 | case MBEDTLS_MD_SHA384: |
| 137 | s->hash_algo = "sha384"; |
| 138 | s->digest_size = SHA384_SUM_LEN; |
| 139 | break; |
| 140 | case MBEDTLS_MD_SHA512: |
| 141 | s->hash_algo = "sha512"; |
| 142 | s->digest_size = SHA512_SUM_LEN; |
| 143 | break; |
| 144 | /* Unsupported algo */ |
| 145 | case MBEDTLS_MD_MD5: |
| 146 | case MBEDTLS_MD_SHA224: |
| 147 | default: |
| 148 | ret = -EINVAL; |
| 149 | goto error_sig; |
| 150 | } |
| 151 | |
| 152 | /* |
| 153 | * Optional attributes: |
| 154 | * auth_ids holds AuthorityKeyIdentifier (information of issuer), |
| 155 | * aka akid, which is used to match with a cert's id or skid to |
| 156 | * indicate that is the issuer when we lookup a cert chain. |
| 157 | * |
| 158 | * auth_ids[0]: |
| 159 | * [PKCS#7 or CMS ver 1] - generated from "Issuer + Serial number" |
| 160 | * [CMS ver 3] - generated from skid (subjectKeyId) |
| 161 | * auth_ids[1]: generated from skid (subjectKeyId) |
| 162 | * |
| 163 | * Assume that we are using PKCS#7 (msg->version=1), |
| 164 | * not CMS ver 3 (msg->version=3). |
| 165 | */ |
| 166 | akid_len = cert->authority_key_id.authorityCertSerialNumber.len; |
| 167 | akid_data = cert->authority_key_id.authorityCertSerialNumber.p; |
| 168 | |
| 169 | /* Check if serial number exists */ |
| 170 | if (akid_len && akid_data) { |
| 171 | s->auth_ids[0] = asymmetric_key_generate_id(akid_data, |
| 172 | akid_len, |
| 173 | cert->issuer_raw.p, |
| 174 | cert->issuer_raw.len); |
| 175 | if (!s->auth_ids[0]) { |
| 176 | ret = -ENOMEM; |
| 177 | goto error_sig; |
| 178 | } |
| 179 | } |
| 180 | |
| 181 | akid_len = cert->authority_key_id.keyIdentifier.len; |
| 182 | akid_data = cert->authority_key_id.keyIdentifier.p; |
| 183 | |
| 184 | /* Check if subjectKeyId exists */ |
| 185 | if (akid_len && akid_data) { |
| 186 | s->auth_ids[1] = asymmetric_key_generate_id(akid_data, |
| 187 | akid_len, |
| 188 | "", 0); |
| 189 | if (!s->auth_ids[1]) { |
| 190 | ret = -ENOMEM; |
| 191 | goto error_sig; |
| 192 | } |
| 193 | } |
| 194 | |
| 195 | /* |
| 196 | * Encoding can be pkcs1 or raw, but only pkcs1 is supported. |
| 197 | * Set the encoding explicitly to pkcs1. |
| 198 | */ |
| 199 | s->encoding = "pkcs1"; |
| 200 | |
| 201 | /* Copy the signature data */ |
| 202 | s->s = kmemdup(cert->sig.p, cert->sig.len, GFP_KERNEL); |
| 203 | if (!s->s) { |
| 204 | ret = -ENOMEM; |
| 205 | goto error_sig; |
| 206 | } |
| 207 | s->s_size = cert->sig.len; |
| 208 | |
| 209 | /* Calculate the digest of signed data (tbs) */ |
| 210 | s->digest = kzalloc(s->digest_size, GFP_KERNEL); |
| 211 | if (!s->digest) { |
| 212 | ret = -ENOMEM; |
| 213 | goto error_sig; |
| 214 | } |
| 215 | |
| 216 | ret = hash_calculate(s->hash_algo, ®ion, 1, s->digest); |
| 217 | if (!ret) |
| 218 | *sig = s; |
| 219 | |
| 220 | return ret; |
| 221 | |
| 222 | error_sig: |
| 223 | public_key_signature_free(s); |
| 224 | return ret; |
| 225 | } |
| 226 | |
| 227 | static int x509_save_mbedtls_ctx(const mbedtls_x509_crt *cert, |
| 228 | struct x509_cert_mbedtls_ctx **pctx) |
| 229 | { |
| 230 | struct x509_cert_mbedtls_ctx *ctx; |
| 231 | |
| 232 | ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); |
| 233 | if (!ctx) |
| 234 | return -ENOMEM; |
| 235 | |
| 236 | /* Signed data (tbs - The part that is To Be Signed)*/ |
| 237 | ctx->tbs = kmemdup(cert->tbs.p, cert->tbs.len, |
| 238 | GFP_KERNEL); |
| 239 | if (!ctx->tbs) |
| 240 | goto error_ctx; |
| 241 | |
| 242 | /* Raw serial number */ |
| 243 | ctx->raw_serial = kmemdup(cert->serial.p, |
| 244 | cert->serial.len, GFP_KERNEL); |
| 245 | if (!ctx->raw_serial) |
| 246 | goto error_ctx; |
| 247 | |
| 248 | /* Raw issuer */ |
| 249 | ctx->raw_issuer = kmemdup(cert->issuer_raw.p, |
| 250 | cert->issuer_raw.len, GFP_KERNEL); |
| 251 | if (!ctx->raw_issuer) |
| 252 | goto error_ctx; |
| 253 | |
| 254 | /* Raw subject */ |
| 255 | ctx->raw_subject = kmemdup(cert->subject_raw.p, |
| 256 | cert->subject_raw.len, GFP_KERNEL); |
| 257 | if (!ctx->raw_subject) |
| 258 | goto error_ctx; |
| 259 | |
| 260 | /* Raw subjectKeyId */ |
| 261 | ctx->raw_skid = kmemdup(cert->subject_key_id.p, |
| 262 | cert->subject_key_id.len, GFP_KERNEL); |
| 263 | if (!ctx->raw_skid) |
| 264 | goto error_ctx; |
| 265 | |
| 266 | *pctx = ctx; |
| 267 | |
| 268 | return 0; |
| 269 | |
| 270 | error_ctx: |
| 271 | x509_free_mbedtls_ctx(ctx); |
| 272 | return -ENOMEM; |
| 273 | } |
| 274 | |
| 275 | /* |
| 276 | * Free an X.509 certificate |
| 277 | */ |
| 278 | void x509_free_certificate(struct x509_certificate *cert) |
| 279 | { |
| 280 | if (cert) { |
| 281 | public_key_free(cert->pub); |
| 282 | public_key_signature_free(cert->sig); |
| 283 | kfree(cert->issuer); |
| 284 | kfree(cert->subject); |
| 285 | kfree(cert->id); |
| 286 | kfree(cert->skid); |
| 287 | x509_free_mbedtls_ctx(cert->mbedtls_ctx); |
| 288 | kfree(cert); |
| 289 | } |
| 290 | } |
| 291 | |
| 292 | int x509_populate_pubkey(mbedtls_x509_crt *cert, struct public_key **pub_key) |
| 293 | { |
| 294 | struct public_key *pk; |
| 295 | |
| 296 | pk = kzalloc(sizeof(*pk), GFP_KERNEL); |
| 297 | if (!pk) |
| 298 | return -ENOMEM; |
| 299 | |
| 300 | pk->key = kzalloc(cert->pk_raw.len, GFP_KERNEL); |
| 301 | if (!pk->key) { |
| 302 | kfree(pk); |
| 303 | return -ENOMEM; |
| 304 | } |
| 305 | memcpy(pk->key, cert->pk_raw.p, cert->pk_raw.len); |
| 306 | pk->keylen = cert->pk_raw.len; |
| 307 | |
| 308 | /* |
| 309 | * For ECC keys, params field might include information about the curve used, |
| 310 | * the generator point, or other algorithm-specific parameters. |
| 311 | * For RSA keys, it's common for the params field to be NULL. |
| 312 | * FIXME: Assume that we just support RSA keys with id_type X509. |
| 313 | */ |
| 314 | pk->params = NULL; |
| 315 | pk->paramlen = 0; |
| 316 | |
| 317 | pk->key_is_private = false; |
| 318 | pk->id_type = "X509"; |
| 319 | pk->pkey_algo = "rsa"; |
| 320 | pk->algo = OID_rsaEncryption; |
| 321 | |
| 322 | *pub_key = pk; |
| 323 | |
| 324 | return 0; |
| 325 | } |
| 326 | |
| 327 | int x509_populate_cert(mbedtls_x509_crt *mbedtls_cert, |
| 328 | struct x509_certificate **pcert) |
| 329 | { |
| 330 | struct x509_certificate *cert; |
| 331 | struct asymmetric_key_id *kid; |
| 332 | struct asymmetric_key_id *skid; |
| 333 | int ret; |
| 334 | |
| 335 | cert = kzalloc(sizeof(*cert), GFP_KERNEL); |
| 336 | if (!cert) |
| 337 | return -ENOMEM; |
| 338 | |
| 339 | /* Public key details */ |
| 340 | ret = x509_populate_pubkey(mbedtls_cert, &cert->pub); |
| 341 | if (ret) |
| 342 | goto error_cert_pop; |
| 343 | |
| 344 | /* Signature parameters */ |
| 345 | ret = x509_populate_signature_params(mbedtls_cert, &cert->sig); |
| 346 | if (ret) |
| 347 | goto error_cert_pop; |
| 348 | |
| 349 | ret = -ENOMEM; |
| 350 | |
| 351 | /* Name of certificate issuer */ |
| 352 | cert->issuer = x509_populate_dn_name_string(&mbedtls_cert->issuer); |
| 353 | if (!cert->issuer) |
| 354 | goto error_cert_pop; |
| 355 | |
| 356 | /* Name of certificate subject */ |
| 357 | cert->subject = x509_populate_dn_name_string(&mbedtls_cert->subject); |
| 358 | if (!cert->subject) |
| 359 | goto error_cert_pop; |
| 360 | |
| 361 | /* Certificate validity */ |
| 362 | cert->valid_from = x509_get_timestamp(&mbedtls_cert->valid_from); |
| 363 | cert->valid_to = x509_get_timestamp(&mbedtls_cert->valid_to); |
| 364 | |
| 365 | /* Save mbedtls context we need */ |
| 366 | ret = x509_save_mbedtls_ctx(mbedtls_cert, &cert->mbedtls_ctx); |
| 367 | if (ret) |
| 368 | goto error_cert_pop; |
| 369 | |
| 370 | /* Signed data (tbs - The part that is To Be Signed)*/ |
| 371 | cert->tbs = cert->mbedtls_ctx->tbs; |
| 372 | cert->tbs_size = mbedtls_cert->tbs.len; |
| 373 | |
| 374 | /* Raw serial number */ |
| 375 | cert->raw_serial = cert->mbedtls_ctx->raw_serial; |
| 376 | cert->raw_serial_size = mbedtls_cert->serial.len; |
| 377 | |
| 378 | /* Raw issuer */ |
| 379 | cert->raw_issuer = cert->mbedtls_ctx->raw_issuer; |
| 380 | cert->raw_issuer_size = mbedtls_cert->issuer_raw.len; |
| 381 | |
| 382 | /* Raw subject */ |
| 383 | cert->raw_subject = cert->mbedtls_ctx->raw_subject; |
| 384 | cert->raw_subject_size = mbedtls_cert->subject_raw.len; |
| 385 | |
| 386 | /* Raw subjectKeyId */ |
| 387 | cert->raw_skid = cert->mbedtls_ctx->raw_skid; |
| 388 | cert->raw_skid_size = mbedtls_cert->subject_key_id.len; |
| 389 | |
| 390 | /* Generate cert issuer + serial number key ID */ |
| 391 | kid = asymmetric_key_generate_id(cert->raw_serial, |
| 392 | cert->raw_serial_size, |
| 393 | cert->raw_issuer, |
| 394 | cert->raw_issuer_size); |
| 395 | if (IS_ERR(kid)) { |
| 396 | ret = PTR_ERR(kid); |
| 397 | goto error_cert_pop; |
| 398 | } |
| 399 | cert->id = kid; |
| 400 | |
| 401 | /* Generate subject + subjectKeyId */ |
| 402 | skid = asymmetric_key_generate_id(cert->raw_skid, cert->raw_skid_size, "", 0); |
| 403 | if (IS_ERR(skid)) { |
| 404 | ret = PTR_ERR(skid); |
| 405 | goto error_cert_pop; |
| 406 | } |
| 407 | cert->skid = skid; |
| 408 | |
| 409 | /* |
| 410 | * Set the certificate flags: |
| 411 | * self_signed, unsupported_key, unsupported_sig, blacklisted |
| 412 | */ |
| 413 | ret = x509_set_cert_flags(cert); |
| 414 | if (!ret) { |
| 415 | *pcert = cert; |
| 416 | return 0; |
| 417 | } |
| 418 | |
| 419 | error_cert_pop: |
| 420 | x509_free_certificate(cert); |
| 421 | return ret; |
| 422 | } |
| 423 | |
| 424 | struct x509_certificate *x509_cert_parse(const void *data, size_t datalen) |
| 425 | { |
| 426 | mbedtls_x509_crt mbedtls_cert; |
| 427 | struct x509_certificate *cert = NULL; |
| 428 | long ret; |
| 429 | |
| 430 | /* Parse DER encoded certificate */ |
| 431 | mbedtls_x509_crt_init(&mbedtls_cert); |
| 432 | ret = mbedtls_x509_crt_parse_der(&mbedtls_cert, data, datalen); |
| 433 | if (ret) |
| 434 | goto clean_up_ctx; |
| 435 | |
| 436 | /* Populate x509_certificate from mbedtls_x509_crt */ |
| 437 | ret = x509_populate_cert(&mbedtls_cert, &cert); |
| 438 | if (ret) |
| 439 | goto clean_up_ctx; |
| 440 | |
| 441 | clean_up_ctx: |
| 442 | mbedtls_x509_crt_free(&mbedtls_cert); |
| 443 | if (!ret) |
| 444 | return cert; |
| 445 | |
| 446 | return ERR_PTR(ret); |
| 447 | } |