From: tb Date: Wed, 28 Aug 2024 07:15:04 +0000 (+0000) Subject: Implement X509_get_signature_info() X-Git-Url: http://artulab.com/gitweb/?a=commitdiff_plain;h=c5d7bed5dd774f32fe3ef5eeee814d3acfe35920;p=openbsd Implement X509_get_signature_info() This is a slightly strange combination of OBJ_find_sigid_algs() and the security level API necessary because OBJ_find_sigid_algs() on its own isn't smart enough for the special needs of RSA-PSS and EdDSA. The API extracts the hash's NID and the pubkey's NID from the certificate's signatureAlgorithm and invokes special handlers for RSA-PSS and EdDSA for retrieving the corresponding information. This isn't entirely free for RSA-PSS, but for now we don't cache this information. The security bits calculation is a bit hand-wavy, but that's something that comes along with this sort of numerology. ok jsing --- diff --git a/lib/libcrypto/Makefile b/lib/libcrypto/Makefile index 30c63be8e4b..b4407d566c1 100644 --- a/lib/libcrypto/Makefile +++ b/lib/libcrypto/Makefile @@ -1,4 +1,4 @@ -# $OpenBSD: Makefile,v 1.202 2024/08/10 06:41:49 tb Exp $ +# $OpenBSD: Makefile,v 1.203 2024/08/28 07:15:04 tb Exp $ LIB= crypto LIBREBUILD=y @@ -589,6 +589,7 @@ SRCS+= x509_purp.c SRCS+= x509_r2x.c SRCS+= x509_req.c SRCS+= x509_set.c +SRCS+= x509_siginfo.c SRCS+= x509_skey.c SRCS+= x509_trs.c SRCS+= x509_txt.c diff --git a/lib/libcrypto/ec/ecx_methods.c b/lib/libcrypto/ec/ecx_methods.c index 70475e8dc1f..6b5759d4fae 100644 --- a/lib/libcrypto/ec/ecx_methods.c +++ b/lib/libcrypto/ec/ecx_methods.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ecx_methods.c,v 1.13 2024/04/02 04:04:07 tb Exp $ */ +/* $OpenBSD: ecx_methods.c,v 1.14 2024/08/28 07:15:04 tb Exp $ */ /* * Copyright (c) 2022 Joel Sing * @@ -509,6 +509,24 @@ ecx_security_bits(const EVP_PKEY *pkey) return 0; } +static int +ecx_signature_info(const X509_ALGOR *algor, int *md_nid, int *pkey_nid, + int *security_bits, uint32_t *flags) +{ + const ASN1_OBJECT *aobj; + + X509_ALGOR_get0(&aobj, NULL, NULL, algor); + if (OBJ_obj2nid(aobj) != EVP_PKEY_ED25519) + return 0; + + *md_nid = NID_undef; + *pkey_nid = NID_ED25519; + *security_bits = ED25519_SECURITY_BITS; + *flags = X509_SIG_INFO_TLS | X509_SIG_INFO_VALID; + + return 1; +} + static int ecx_param_cmp(const EVP_PKEY *pkey1, const EVP_PKEY *pkey2) { @@ -929,6 +947,8 @@ const EVP_PKEY_ASN1_METHOD ed25519_asn1_meth = { .pkey_bits = ecx_bits, .pkey_security_bits = ecx_security_bits, + .signature_info = ecx_signature_info, + .param_cmp = ecx_param_cmp, .pkey_free = ecx_free, diff --git a/lib/libcrypto/evp/evp_local.h b/lib/libcrypto/evp/evp_local.h index 3e90e068e66..5d541ffec4d 100644 --- a/lib/libcrypto/evp/evp_local.h +++ b/lib/libcrypto/evp/evp_local.h @@ -1,4 +1,4 @@ -/* $OpenBSD: evp_local.h,v 1.23 2024/08/22 12:24:24 tb Exp $ */ +/* $OpenBSD: evp_local.h,v 1.24 2024/08/28 07:15:04 tb Exp $ */ /* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL * project 2000. */ @@ -112,6 +112,9 @@ struct evp_pkey_asn1_method_st { int (*pkey_bits)(const EVP_PKEY *pk); int (*pkey_security_bits)(const EVP_PKEY *pk); + int (*signature_info)(const X509_ALGOR *sig_alg, int *out_md_nid, + int *out_pkey_nid, int *out_security_bits, uint32_t *out_flags); + int (*param_decode)(EVP_PKEY *pkey, const unsigned char **pder, int derlen); int (*param_encode)(const EVP_PKEY *pkey, unsigned char **pder); diff --git a/lib/libcrypto/rsa/rsa_ameth.c b/lib/libcrypto/rsa/rsa_ameth.c index c722188c436..d7ce9317338 100644 --- a/lib/libcrypto/rsa/rsa_ameth.c +++ b/lib/libcrypto/rsa/rsa_ameth.c @@ -1,4 +1,4 @@ -/* $OpenBSD: rsa_ameth.c,v 1.58 2024/03/17 07:10:00 tb Exp $ */ +/* $OpenBSD: rsa_ameth.c,v 1.59 2024/08/28 07:15:04 tb Exp $ */ /* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL * project 2006. */ @@ -845,6 +845,58 @@ rsa_pss_get_param(const RSA_PSS_PARAMS *pss, const EVP_MD **pmd, return 1; } +static int +rsa_pss_signature_info(const X509_ALGOR *alg, int *out_md_nid, + int *out_pkey_nid, int *out_security_bits, uint32_t *out_flags) +{ + RSA_PSS_PARAMS *pss = NULL; + const ASN1_OBJECT *aobj; + const EVP_MD *md, *mgf1md; + int md_len, salt_len; + int md_nid = NID_undef, pkey_nid = NID_undef; + int security_bits = -1; + uint32_t flags = 0; + + X509_ALGOR_get0(&aobj, NULL, NULL, alg); + if (OBJ_obj2nid(aobj) != EVP_PKEY_RSA_PSS) + goto err; + + if ((pss = rsa_pss_decode(alg)) == NULL) + goto err; + if (!rsa_pss_get_param(pss, &md, &mgf1md, &salt_len)) + goto err; + + if ((md_nid = EVP_MD_type(md)) == NID_undef) + goto err; + if ((md_len = EVP_MD_size(md)) <= 0) + goto err; + + /* + * RFC 8446, section 4.2.3 - restricts the digest algorithm: + * - it must be one of SHA256, SHA384, and SHA512; + * - the same digest must be used in the mask generation function; + * - the salt length must match the output length of the digest. + * XXX - consider separate flags for these checks. + */ + if (md_nid == NID_sha256 || md_nid == NID_sha384 || md_nid == NID_sha512) { + if (md_nid == EVP_MD_type(mgf1md) && salt_len == md_len) + flags |= X509_SIG_INFO_TLS; + } + + security_bits = md_len * 4; + flags |= X509_SIG_INFO_VALID; + + *out_md_nid = md_nid; + *out_pkey_nid = pkey_nid; + *out_security_bits = security_bits; + *out_flags = flags; + + err: + RSA_PSS_PARAMS_free(pss); + + return (flags & X509_SIG_INFO_VALID) != 0; +} + #ifndef OPENSSL_NO_CMS static int rsa_cms_verify(CMS_SignerInfo *si) @@ -1216,6 +1268,8 @@ const EVP_PKEY_ASN1_METHOD rsa_pss_asn1_meth = { .pkey_bits = rsa_bits, .pkey_security_bits = rsa_security_bits, + .signature_info = rsa_pss_signature_info, + .sig_print = rsa_sig_print, .pkey_free = rsa_free, diff --git a/lib/libcrypto/x509/x509.h b/lib/libcrypto/x509/x509.h index 87bc6dbb337..856ad19ba45 100644 --- a/lib/libcrypto/x509/x509.h +++ b/lib/libcrypto/x509/x509.h @@ -1,4 +1,4 @@ -/* $OpenBSD: x509.h,v 1.112 2024/06/12 03:55:46 tb Exp $ */ +/* $OpenBSD: x509.h,v 1.113 2024/08/28 07:15:04 tb Exp $ */ /* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) * All rights reserved. * @@ -622,6 +622,14 @@ X509 * d2i_X509_AUX(X509 **a,const unsigned char **pp,long length); int i2d_re_X509_tbs(X509 *x, unsigned char **pp); +#if defined(LIBRESSL_INTERNAL) || defined(LIBRESSL_NEXT_API) +/* Flags returned by X509_get_signature_info(): valid and suitable for TLS. */ +#define X509_SIG_INFO_VALID 1 +#define X509_SIG_INFO_TLS 2 +int X509_get_signature_info(X509 *x, int *mdnid, int *pknid, int *secbits, + uint32_t *flags); +#endif + void X509_get0_signature(const ASN1_BIT_STRING **psig, const X509_ALGOR **palg, const X509 *x); int X509_get_signature_nid(const X509 *x); diff --git a/lib/libcrypto/x509/x509_siginfo.c b/lib/libcrypto/x509/x509_siginfo.c new file mode 100644 index 00000000000..9bbb1332162 --- /dev/null +++ b/lib/libcrypto/x509/x509_siginfo.c @@ -0,0 +1,113 @@ +/* $OpenBSD: x509_siginfo.c,v 1.1 2024/08/28 07:15:04 tb Exp $ */ + +/* + * Copyright (c) 2024 Theo Buehler + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include + +#include "evp_local.h" + +#include "x509_internal.h" + +static int +x509_find_sigid_algs(const X509 *x509, int *out_md_nid, int *out_pkey_nid) +{ + const ASN1_OBJECT *aobj; + int nid; + + *out_md_nid = NID_undef; + *out_pkey_nid = NID_undef; + + X509_ALGOR_get0(&aobj, NULL, NULL, x509->sig_alg); + if ((nid = OBJ_obj2nid(aobj)) == NID_undef) + return 0; + + return OBJ_find_sigid_algs(nid, out_md_nid, out_pkey_nid); +} + +int +X509_get_signature_info(X509 *x509, int *out_md_nid, int *out_pkey_nid, + int *out_security_bits, uint32_t *out_flags) +{ + const EVP_MD *md; + int md_nid = NID_undef, pkey_nid = NID_undef, security_bits = -1; + uint32_t flags = 0; + + if (out_md_nid != NULL) + *out_md_nid = md_nid; + if (out_pkey_nid != NULL) + *out_pkey_nid = pkey_nid; + if (out_security_bits != NULL) + *out_security_bits = security_bits; + if (out_flags != NULL) + *out_flags = flags; + + if (!x509v3_cache_extensions(x509)) + goto err; + + if (!x509_find_sigid_algs(x509, &md_nid, &pkey_nid)) + goto err; + + /* + * If md_nid == NID_undef, this means we need to consult the ameth. + * Handlers are available for EdDSA and RSA-PSS. No other signature + * algorithm with NID_undef should appear in a certificate. + */ + if (md_nid == NID_undef) { + const EVP_PKEY_ASN1_METHOD *ameth; + + if ((ameth = EVP_PKEY_asn1_find(NULL, pkey_nid)) == NULL || + ameth->signature_info == NULL) + goto err; + + if (!ameth->signature_info(x509->sig_alg, &md_nid, &pkey_nid, + &security_bits, &flags)) + goto err; + + goto done; + } + + /* XXX - OpenSSL 3 special cases SHA-1 (63 bits) and MD5 (39 bits). */ + if ((md = EVP_get_digestbynid(md_nid)) == NULL) + goto err; + + /* Assume 4 bits of collision resistance per octet. */ + if ((security_bits = EVP_MD_size(md)) <= 0) + goto err; + security_bits *= 4; + + if (md_nid == NID_sha1 || md_nid == NID_sha256 || + md_nid == NID_sha384 || md_nid == NID_sha512) + flags |= X509_SIG_INFO_TLS; + + flags |= X509_SIG_INFO_VALID; + + done: + if (out_md_nid != NULL) + *out_md_nid = md_nid; + if (out_pkey_nid != NULL) + *out_pkey_nid = pkey_nid; + if (out_security_bits != NULL) + *out_security_bits = security_bits; + if (out_flags != NULL) + *out_flags = flags; + + err: + return (flags & X509_SIG_INFO_VALID) != 0; +} +LCRYPTO_ALIAS(X509_get_signature_info);