From 284c6bbf718f984cb47bdcae8e03cbb111558302 Mon Sep 17 00:00:00 2001 From: tb Date: Mon, 27 Jun 2022 14:10:22 +0000 Subject: [PATCH] Allow security_level to mestastasize into the verifier The tentacles are everywhere. This checks that all certs in a chain have keys and signature algorithms matching the requirements of the security_level configured in the verify parameters. ok beck jsing --- lib/libcrypto/x509/x509_internal.h | 3 +- lib/libcrypto/x509/x509_verify.c | 5 +- lib/libcrypto/x509/x509_vfy.c | 143 ++++++++++++++++++++++++++++- lib/libcrypto/x509/x509_vfy.h | 9 +- 4 files changed, 156 insertions(+), 4 deletions(-) diff --git a/lib/libcrypto/x509/x509_internal.h b/lib/libcrypto/x509/x509_internal.h index c6ce5229ade..030f24c4701 100644 --- a/lib/libcrypto/x509/x509_internal.h +++ b/lib/libcrypto/x509/x509_internal.h @@ -1,4 +1,4 @@ -/* $OpenBSD: x509_internal.h,v 1.18 2022/03/14 21:15:49 tb Exp $ */ +/* $OpenBSD: x509_internal.h,v 1.19 2022/06/27 14:10:22 tb Exp $ */ /* * Copyright (c) 2020 Bob Beck * @@ -135,6 +135,7 @@ int x509_constraints_check(struct x509_constraints_names *names, int x509_constraints_chain(STACK_OF(X509) *chain, int *error, int *depth); void x509_verify_cert_info_populate(X509 *cert); +int x509_vfy_check_security_level(X509_STORE_CTX *ctx); __END_HIDDEN_DECLS diff --git a/lib/libcrypto/x509/x509_verify.c b/lib/libcrypto/x509/x509_verify.c index f6959d1f3af..83030672efb 100644 --- a/lib/libcrypto/x509/x509_verify.c +++ b/lib/libcrypto/x509/x509_verify.c @@ -1,4 +1,4 @@ -/* $OpenBSD: x509_verify.c,v 1.56 2022/06/25 20:01:43 beck Exp $ */ +/* $OpenBSD: x509_verify.c,v 1.57 2022/06/27 14:10:22 tb Exp $ */ /* * Copyright (c) 2020-2021 Bob Beck * @@ -415,6 +415,9 @@ x509_verify_ctx_validate_legacy_chain(struct x509_verify_ctx *ctx, goto err; #endif + if (!x509_vfy_check_security_level(ctx->xsc)) + goto err; + if (!x509_constraints_chain(ctx->xsc->chain, &ctx->xsc->error, &ctx->xsc->error_depth)) { X509 *cert = sk_X509_value(ctx->xsc->chain, depth); diff --git a/lib/libcrypto/x509/x509_vfy.c b/lib/libcrypto/x509/x509_vfy.c index 9a929963f3d..18cf08a07c4 100644 --- a/lib/libcrypto/x509/x509_vfy.c +++ b/lib/libcrypto/x509/x509_vfy.c @@ -1,4 +1,4 @@ -/* $OpenBSD: x509_vfy.c,v 1.101 2022/01/22 00:36:46 inoguchi Exp $ */ +/* $OpenBSD: x509_vfy.c,v 1.102 2022/06/27 14:10:22 tb Exp $ */ /* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) * All rights reserved. * @@ -144,6 +144,8 @@ static int X509_cmp_time_internal(const ASN1_TIME *ctm, time_t *cmp_time, static int internal_verify(X509_STORE_CTX *ctx); static int get_issuer_sk(X509 **issuer, X509_STORE_CTX *ctx, X509 *x); +static int check_key_level(X509_STORE_CTX *ctx, X509 *cert); +static int verify_cb_cert(X509_STORE_CTX *ctx, X509 *x, int depth, int err); int ASN1_time_tm_clamp_notafter(struct tm *tm); @@ -542,6 +544,11 @@ X509_verify_cert_legacy(X509_STORE_CTX *ctx) if (!ok) goto end; + /* Check that the chain satisfies the security level. */ + ok = x509_vfy_check_security_level(ctx); + if (!ok) + goto end; + /* Check name constraints */ ok = check_name_constraints(ctx); if (!ok) @@ -628,6 +635,14 @@ X509_verify_cert(X509_STORE_CTX *ctx) return -1; } + /* + * If the certificate's public key is too weak, don't bother + * continuing. + */ + if (!check_key_level(ctx, ctx->cert) && + !verify_cb_cert(ctx, ctx->cert, 0, X509_V_ERR_EE_KEY_TOO_SMALL)) + return 0; + /* * If flags request legacy, use the legacy verifier. If we * requested "no alt chains" from the age of hammer pants, use @@ -2596,3 +2611,129 @@ X509_STORE_CTX_set0_param(X509_STORE_CTX *ctx, X509_VERIFY_PARAM *param) X509_VERIFY_PARAM_free(ctx->param); ctx->param = param; } + +/* + * Check if |bits| are adequate for |security level|. + * Returns 1 if ok, 0 otherwise. + */ +static int +enough_bits_for_security_level(int bits, int level) +{ + /* + * Sigh. OpenSSL does this silly squashing, so we will + * too. Derp for Derp compatibility being important. + */ + if (level < 0) + level = 0; + if (level > 5) + level = 5; + + switch (level) { + case 0: + return 1; + case 1: + return bits >= 80; + case 2: + return bits >= 112; + case 3: + return bits >= 128; + case 4: + return bits >= 192; + case 5: + return bits >= 256; + default: + return 0; + } +} + +/* + * Check whether the public key of |cert| meets the security level of |ctx|. + * + * Returns 1 on success, 0 otherwise. + */ +static int +check_key_level(X509_STORE_CTX *ctx, X509 *cert) +{ + EVP_PKEY *pkey; + int bits; + + /* Unsupported or malformed keys are not secure */ + if ((pkey = X509_get0_pubkey(cert)) == NULL) + return 0; + + if ((bits = EVP_PKEY_security_bits(pkey)) <= 0) + return 0; + + return enough_bits_for_security_level(bits, ctx->param->security_level); +} + +/* + * Check whether the signature digest algorithm of |cert| meets the security + * level of |ctx|. Do not check trust anchors (self-signed or not). + * + * Returns 1 on success, 0 otherwise. + */ +static int +check_sig_level(X509_STORE_CTX *ctx, X509 *cert) +{ + const EVP_MD *md; + int bits, nid, md_nid; + + if ((nid = X509_get_signature_nid(cert)) == NID_undef) + return 0; + + /* + * Look up signature algorithm digest. + */ + + if (!OBJ_find_sigid_algs(nid, &md_nid, NULL)) + return 0; + + if (md_nid == NID_undef) + return 0; + + if ((md = EVP_get_digestbynid(md_nid)) == NULL) + return 0; + + /* Assume 4 bits of collision resistance for each hash octet. */ + bits = EVP_MD_size(md) * 4; + + return enough_bits_for_security_level(bits, ctx->param->security_level); +} + +int +x509_vfy_check_security_level(X509_STORE_CTX *ctx) +{ + int num = sk_X509_num(ctx->chain); + int i; + + if (ctx->param->security_level <= 0) + return 1; + + for (i = 0; i < num; i++) { + X509 *cert = sk_X509_value(ctx->chain, i); + + /* + * We've already checked the security of the leaf key, so here + * we only check the security of issuer keys. + */ + if (i > 0) { + if (!check_key_level(ctx, cert) && + !verify_cb_cert(ctx, cert, i, + X509_V_ERR_CA_KEY_TOO_SMALL)) + return 0; + } + + /* + * We also check the signature algorithm security of all certs + * except those of the trust anchor at index num - 1. + */ + if (i == num - 1) + break; + + if (!check_sig_level(ctx, cert) && + !verify_cb_cert(ctx, cert, i, X509_V_ERR_CA_MD_TOO_WEAK)) + return 0; + } + return 1; +} diff --git a/lib/libcrypto/x509/x509_vfy.h b/lib/libcrypto/x509/x509_vfy.h index a007fe47953..d31e779aae7 100644 --- a/lib/libcrypto/x509/x509_vfy.h +++ b/lib/libcrypto/x509/x509_vfy.h @@ -1,4 +1,4 @@ -/* $OpenBSD: x509_vfy.h,v 1.51 2022/06/27 14:00:09 tb Exp $ */ +/* $OpenBSD: x509_vfy.h,v 1.52 2022/06/27 14:10:22 tb Exp $ */ /* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) * All rights reserved. * @@ -205,6 +205,13 @@ void X509_STORE_CTX_set_depth(X509_STORE_CTX *ctx, int depth); /* Issuer lookup error */ #define X509_V_ERR_STORE_LOOKUP 66 +#if defined(LIBRESSL_INTERNAL) +/* Security level errors */ +#define X509_V_ERR_EE_KEY_TOO_SMALL 67 +#define X509_V_ERR_CA_KEY_TOO_SMALL 68 +#define X509_V_ERR_CA_MD_TOO_WEAK 69 +#endif + /* Certificate verify flags */ /* Send issuer+subject checks to verify_cb */ -- 2.20.1