From 0c52b531c6737710b2b003d471a39907bdfbf5b5 Mon Sep 17 00:00:00 2001 From: Damian Hobson-Garcia Date: Thu, 13 May 2021 11:53:59 +0900 Subject: [PATCH 01/12] Add RFC 5755 attribute certificate support Add support for attribute certificates (v2) as described in RFC 5755 profile. Attribute certificates provide a mechanism to manage authorization information separately from the identity information provided by public key certificates. This initial patch adds the ASN.1 definitions and I/O API. Accessor functions for the certificate fields will be added in subsequent patches. --- build.info | 2 + crypto/asn1/asn1_item_list.c | 1 + crypto/asn1/asn1_item_list.h | 1 + crypto/x509/build.info | 3 +- crypto/x509/x509_acert.c | 75 ++++++++++++++++++++++++++++ crypto/x509/x509_acert.h | 22 ++++++++ crypto/x509/x_all.c | 23 +++++++++ doc/man3/PEM_read_bio_PrivateKey.pod | 12 +++++ doc/man3/X509_dup.pod | 13 +++++ doc/man3/d2i_X509.pod | 6 +++ include/crypto/x509_acert.h | 66 ++++++++++++++++++++++++ include/openssl/pem.h | 1 + include/openssl/x509_acert.h.in | 48 ++++++++++++++++++ util/libcrypto.num | 23 +++++++++ 14 files changed, 295 insertions(+), 1 deletion(-) create mode 100644 crypto/x509/x509_acert.c create mode 100644 crypto/x509/x509_acert.h create mode 100644 include/crypto/x509_acert.h create mode 100644 include/openssl/x509_acert.h.in diff --git a/build.info b/build.info index fdab98c103d6f..51d9184045bf5 100644 --- a/build.info +++ b/build.info @@ -44,6 +44,7 @@ DEPEND[]=include/openssl/asn1.h \ include/openssl/ui.h \ include/openssl/x509.h \ include/openssl/x509v3.h \ + include/openssl/x509_acert.h \ include/openssl/x509_vfy.h \ include/crypto/bn_conf.h include/crypto/dso_conf.h \ include/internal/param_names.h crypto/params_idx.c @@ -75,6 +76,7 @@ GENERATE[include/openssl/ssl.h]=include/openssl/ssl.h.in GENERATE[include/openssl/ui.h]=include/openssl/ui.h.in GENERATE[include/openssl/x509.h]=include/openssl/x509.h.in GENERATE[include/openssl/x509v3.h]=include/openssl/x509v3.h.in +GENERATE[include/openssl/x509_acert.h]=include/openssl/x509_acert.h.in GENERATE[include/openssl/x509_vfy.h]=include/openssl/x509_vfy.h.in GENERATE[include/crypto/bn_conf.h]=include/crypto/bn_conf.h.in GENERATE[include/crypto/dso_conf.h]=include/crypto/dso_conf.h.in diff --git a/crypto/asn1/asn1_item_list.c b/crypto/asn1/asn1_item_list.c index b5a83ba8914b9..ab0d500d650b0 100644 --- a/crypto/asn1/asn1_item_list.c +++ b/crypto/asn1/asn1_item_list.c @@ -21,6 +21,7 @@ #include #include #include +#include #include "asn1_item_list.h" diff --git a/crypto/asn1/asn1_item_list.h b/crypto/asn1/asn1_item_list.h index 72299a7b6b3de..fc59c2cdbaba6 100644 --- a/crypto/asn1/asn1_item_list.h +++ b/crypto/asn1/asn1_item_list.h @@ -150,6 +150,7 @@ static ASN1_ITEM_EXP *asn1_item_list[] = { ASN1_ITEM_ref(SXNET), ASN1_ITEM_ref(ISSUER_SIGN_TOOL), ASN1_ITEM_ref(USERNOTICE), + ASN1_ITEM_ref(X509_ACERT), ASN1_ITEM_ref(X509_ALGORS), ASN1_ITEM_ref(X509_ALGOR), ASN1_ITEM_ref(X509_ATTRIBUTE), diff --git a/crypto/x509/build.info b/crypto/x509/build.info index 3f70f3ff36dfb..2976b37e0fcdc 100644 --- a/crypto/x509/build.info +++ b/crypto/x509/build.info @@ -15,7 +15,8 @@ SOURCE[../../libcrypto]=\ v3_pcia.c v3_pci.c v3_ist.c \ pcy_cache.c pcy_node.c pcy_data.c pcy_map.c pcy_tree.c pcy_lib.c \ v3_asid.c v3_addr.c v3_tlsf.c v3_admis.c v3_no_rev_avail.c \ - v3_soa_id.c v3_no_ass.c v3_group_ac.c v3_single_use.c v3_ind_iss.c + v3_soa_id.c v3_no_ass.c v3_group_ac.c v3_single_use.c v3_ind_iss.c \ + x509_acert.c IF[{- !$disabled{'deprecated-3.0'} -}] SOURCE[../../libcrypto]=x509type.c diff --git a/crypto/x509/x509_acert.c b/crypto/x509/x509_acert.c new file mode 100644 index 0000000000000..9a1c298d7a5ac --- /dev/null +++ b/crypto/x509/x509_acert.c @@ -0,0 +1,75 @@ +/* + * Copyright 2021 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include +#include +#include +#include "x509_acert.h" + +/* + * OpenSSL ASN.1 template translation of RFC 5755 4.1. + */ + +ASN1_SEQUENCE(OSSL_OBJECT_DIGEST_INFO) = { + ASN1_EMBED(OSSL_OBJECT_DIGEST_INFO, digestedObjectType, ASN1_ENUMERATED), + ASN1_OPT(OSSL_OBJECT_DIGEST_INFO, otherObjectTypeID, ASN1_OBJECT), + ASN1_EMBED(OSSL_OBJECT_DIGEST_INFO, digestAlgorithm, X509_ALGOR), + ASN1_EMBED(OSSL_OBJECT_DIGEST_INFO, objectDigest, ASN1_BIT_STRING), +} ASN1_SEQUENCE_END(OSSL_OBJECT_DIGEST_INFO) + +ASN1_SEQUENCE(OSSL_ISSUER_SERIAL) = { + ASN1_SEQUENCE_OF(OSSL_ISSUER_SERIAL, issuer, GENERAL_NAME), + ASN1_EMBED(OSSL_ISSUER_SERIAL, serial, ASN1_INTEGER), + ASN1_OPT(OSSL_ISSUER_SERIAL, issuerUID, ASN1_BIT_STRING), +} ASN1_SEQUENCE_END(OSSL_ISSUER_SERIAL) + +ASN1_SEQUENCE(X509_ACERT_ISSUER_V2FORM) = { + ASN1_SEQUENCE_OF_OPT(X509_ACERT_ISSUER_V2FORM, issuerName, GENERAL_NAME), + ASN1_IMP_OPT(X509_ACERT_ISSUER_V2FORM, baseCertificateId, OSSL_ISSUER_SERIAL, 0), + ASN1_IMP_OPT(X509_ACERT_ISSUER_V2FORM, objectDigestInfo, OSSL_OBJECT_DIGEST_INFO, 1), +} ASN1_SEQUENCE_END(X509_ACERT_ISSUER_V2FORM) + +ASN1_CHOICE(X509_ACERT_ISSUER) = { + ASN1_SEQUENCE_OF(X509_ACERT_ISSUER, u.v1Form, GENERAL_NAME), + ASN1_IMP(X509_ACERT_ISSUER, u.v2Form, X509_ACERT_ISSUER_V2FORM, 0), +} ASN1_CHOICE_END(X509_ACERT_ISSUER) + +ASN1_SEQUENCE(X509_HOLDER) = { + ASN1_IMP_OPT(X509_HOLDER, baseCertificateID, OSSL_ISSUER_SERIAL, 0), + ASN1_IMP_SEQUENCE_OF_OPT(X509_HOLDER, entityName, GENERAL_NAME, 1), + ASN1_IMP_OPT(X509_HOLDER, objectDigestInfo, OSSL_OBJECT_DIGEST_INFO, 2), +} ASN1_SEQUENCE_END(X509_HOLDER) + +ASN1_SEQUENCE(X509_ACERT_INFO) = { + ASN1_EMBED(X509_ACERT_INFO, version, ASN1_INTEGER), + ASN1_EMBED(X509_ACERT_INFO, holder, X509_HOLDER), + ASN1_EMBED(X509_ACERT_INFO, issuer, X509_ACERT_ISSUER), + ASN1_EMBED(X509_ACERT_INFO, signature, X509_ALGOR), + ASN1_EMBED(X509_ACERT_INFO, serialNumber, ASN1_INTEGER), + ASN1_EMBED(X509_ACERT_INFO, validityPeriod, X509_VAL), + ASN1_SEQUENCE_OF(X509_ACERT_INFO, attributes, X509_ATTRIBUTE), + ASN1_OPT(X509_ACERT_INFO, issuerUID, ASN1_BIT_STRING), + ASN1_SEQUENCE_OF_OPT(X509_ACERT_INFO, extensions, X509_EXTENSION), +} ASN1_SEQUENCE_END(X509_ACERT_INFO) + +ASN1_SEQUENCE(X509_ACERT) = { + ASN1_SIMPLE(X509_ACERT, acinfo, X509_ACERT_INFO), + ASN1_EMBED(X509_ACERT, sig_alg, X509_ALGOR), + ASN1_EMBED(X509_ACERT, signature, ASN1_BIT_STRING), +} ASN1_SEQUENCE_END(X509_ACERT) + +IMPLEMENT_ASN1_FUNCTIONS(X509_ACERT) +IMPLEMENT_ASN1_DUP_FUNCTION(X509_ACERT) +IMPLEMENT_ASN1_ALLOC_FUNCTIONS(X509_ACERT_INFO) +IMPLEMENT_ASN1_ALLOC_FUNCTIONS(OSSL_ISSUER_SERIAL) +IMPLEMENT_ASN1_ALLOC_FUNCTIONS(OSSL_OBJECT_DIGEST_INFO) +IMPLEMENT_ASN1_ALLOC_FUNCTIONS(X509_ACERT_ISSUER_V2FORM) + +IMPLEMENT_PEM_rw(X509_ACERT, X509_ACERT, PEM_STRING_ACERT, X509_ACERT) + diff --git a/crypto/x509/x509_acert.h b/crypto/x509/x509_acert.h new file mode 100644 index 0000000000000..e2680d329739d --- /dev/null +++ b/crypto/x509/x509_acert.h @@ -0,0 +1,22 @@ +/* + * Copyright 2021 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#ifndef OSSL_CRYPTO_X509_X509_ACERT_H +# define OSSL_CRYPTO_X509_X509_ACERT_H + +#include + +# define X509_ACERT_ISSUER_V2 1 + +DECLARE_ASN1_ITEM(OSSL_OBJECT_DIGEST_INFO) +DECLARE_ASN1_ITEM(OSSL_ISSUER_SERIAL) +DECLARE_ASN1_ITEM(X509_ACERT_ISSUER_V2FORM) +DECLARE_ASN1_ITEM(X509_ACERT_ISSUER) +DECLARE_ASN1_ITEM(X509_HOLDER) +#endif diff --git a/crypto/x509/x_all.c b/crypto/x509/x_all.c index 3e4c852b70a10..95c91a0f206b4 100644 --- a/crypto/x509/x_all.c +++ b/crypto/x509/x_all.c @@ -26,6 +26,7 @@ #include "internal/asn1.h" #include "crypto/pkcs7.h" #include "crypto/x509.h" +#include "crypto/x509_acert.h" #include "crypto/rsa.h" int X509_verify(X509 *a, EVP_PKEY *r) @@ -824,3 +825,25 @@ EVP_PKEY *d2i_PUBKEY_bio(BIO *bp, EVP_PKEY **a) { return ASN1_d2i_bio_of(EVP_PKEY, EVP_PKEY_new, d2i_PUBKEY, bp, a); } + +#ifndef OPENSSL_NO_STDIO +X509_ACERT *d2i_X509_ACERT_fp(FILE *fp, X509_ACERT **acert) +{ + return ASN1_item_d2i_fp(ASN1_ITEM_rptr(X509_ACERT), fp, acert); +} + +int i2d_X509_ACERT_fp(FILE *fp, const X509_ACERT *acert) +{ + return ASN1_item_i2d_fp(ASN1_ITEM_rptr(X509_ACERT), fp, acert); +} +#endif + +X509_ACERT *d2i_X509_ACERT_bio(BIO *bp, X509_ACERT **acert) +{ + return ASN1_item_d2i_bio(ASN1_ITEM_rptr(X509_ACERT), bp, acert); +} + +int i2d_X509_ACERT_bio(BIO *bp, const X509_ACERT *acert) +{ + return ASN1_item_i2d_bio(ASN1_ITEM_rptr(X509_ACERT), bp, acert); +} diff --git a/doc/man3/PEM_read_bio_PrivateKey.pod b/doc/man3/PEM_read_bio_PrivateKey.pod index ac93920addec3..6e521b268f0db 100644 --- a/doc/man3/PEM_read_bio_PrivateKey.pod +++ b/doc/man3/PEM_read_bio_PrivateKey.pod @@ -26,6 +26,8 @@ PEM_write_bio_Parameters, PEM_read_bio_DSAparams, PEM_read_DSAparams, PEM_write_bio_DSAparams, PEM_write_DSAparams, PEM_read_bio_DHparams, PEM_read_DHparams, PEM_write_bio_DHparams, PEM_write_DHparams, PEM_read_bio_X509, PEM_read_X509, PEM_write_bio_X509, PEM_write_X509, +PEM_read_bio_X509_ACERT, PEM_read_X509_ACERT, +PEM_write_bio_X509_ACERT, PEM_write_X509_ACERT, PEM_read_bio_X509_AUX, PEM_read_X509_AUX, PEM_write_bio_X509_AUX, PEM_write_X509_AUX, PEM_read_bio_X509_REQ, PEM_read_X509_REQ, PEM_write_bio_X509_REQ, PEM_write_X509_REQ, PEM_write_bio_X509_REQ_NEW, @@ -108,6 +110,13 @@ PEM_write_bio_PKCS7, PEM_write_PKCS7 - PEM routines int PEM_write_bio_X509(BIO *bp, X509 *x); int PEM_write_X509(FILE *fp, X509 *x); + X509_ACERT *PEM_read_bio_X509_ACERT(BIO *bp, X509_ACERT **x, + pem_password_cb *cb, void *u); + X509_ACERT *PEM_read_X509_ACERT(FILE *fp, X509_ACERT **x, + pem_password_cb *cb, void *u); + int PEM_write_bio_X509_ACERT(BIO *bp, X509_ACERT *x); + int PEM_write_X509_ACERT(FILE *fp, X509_ACERT *x); + X509 *PEM_read_bio_X509_AUX(BIO *bp, X509 **x, pem_password_cb *cb, void *u); X509 *PEM_read_X509_AUX(FILE *fp, X509 **x, pem_password_cb *cb, void *u); int PEM_write_bio_X509_AUX(BIO *bp, X509 *x); @@ -287,6 +296,9 @@ The B functions process an X509 certificate using an X509 structure. They will also process a trusted X509 certificate but any trust settings are discarded. +The B functions process an X509 attribute certificate using +an X509_ACERT structure. + The B functions process a trusted X509 certificate using an X509 structure. diff --git a/doc/man3/X509_dup.pod b/doc/man3/X509_dup.pod index 86a259f025f11..621427e3fbf85 100644 --- a/doc/man3/X509_dup.pod +++ b/doc/man3/X509_dup.pod @@ -171,6 +171,10 @@ OSSL_CRMF_PKIPUBLICATIONINFO_new, OSSL_CRMF_SINGLEPUBINFO_free, OSSL_CRMF_SINGLEPUBINFO_it, OSSL_CRMF_SINGLEPUBINFO_new, +OSSL_ISSUER_SERIAL_free, +OSSL_ISSUER_SERIAL_new, +OSSL_OBJECT_DIGEST_INFO_free, +OSSL_OBJECT_DIGEST_INFO_new, OTHERNAME_free, OTHERNAME_new, PBE2PARAM_free, @@ -265,6 +269,15 @@ TS_TST_INFO_free, TS_TST_INFO_new, USERNOTICE_free, USERNOTICE_new, +X509_ACERT_dup, +X509_ACERT_free, +X509_ACERT_it, +X509_ACERT_new, +X509_ACERT_INFO_free, +X509_ACERT_INFO_it, +X509_ACERT_INFO_new, +X509_ACERT_ISSUER_V2FORM_free, +X509_ACERT_ISSUER_V2FORM_new, X509_ALGOR_free, X509_ALGOR_it, X509_ALGOR_new, diff --git a/doc/man3/d2i_X509.pod b/doc/man3/d2i_X509.pod index 00efb60358166..6c4464deb46a8 100644 --- a/doc/man3/d2i_X509.pod +++ b/doc/man3/d2i_X509.pod @@ -157,6 +157,9 @@ d2i_USERNOTICE, d2i_X509, d2i_X509_bio, d2i_X509_fp, +d2i_X509_ACERT, +d2i_X509_ACERT_bio, +d2i_X509_ACERT_fp, d2i_X509_ALGOR, d2i_X509_ALGORS, d2i_X509_ATTRIBUTE, @@ -331,6 +334,9 @@ i2d_USERNOTICE, i2d_X509, i2d_X509_bio, i2d_X509_fp, +i2d_X509_ACERT, +i2d_X509_ACERT_bio, +i2d_X509_ACERT_fp, i2d_X509_ALGOR, i2d_X509_ALGORS, i2d_X509_ATTRIBUTE, diff --git a/include/crypto/x509_acert.h b/include/crypto/x509_acert.h new file mode 100644 index 0000000000000..3223bf623447f --- /dev/null +++ b/include/crypto/x509_acert.h @@ -0,0 +1,66 @@ +/* + * Copyright 2021 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#ifndef OSSL_CRYPTO_X509_ACERT_H +# define OSSL_CRYPTO_X509_ACERT_H +# pragma once + +# include + +struct ossl_object_digest_info_st { + ASN1_ENUMERATED digestedObjectType; + ASN1_OBJECT *otherObjectTypeID; + X509_ALGOR digestAlgorithm; + ASN1_BIT_STRING objectDigest; +}; + +struct ossl_issuer_serial_st { + STACK_OF(GENERAL_NAME) *issuer; + ASN1_INTEGER serial; + ASN1_BIT_STRING *issuerUID; +}; + +struct X509_acert_issuer_v2form_st { + STACK_OF(GENERAL_NAME) *issuerName; + OSSL_ISSUER_SERIAL *baseCertificateId; + OSSL_OBJECT_DIGEST_INFO *objectDigestInfo; +}; + +typedef struct X509_acert_issuer_st { + int type; + union { + STACK_OF(GENERAL_NAME) *v1Form; + X509_ACERT_ISSUER_V2FORM *v2Form; + } u; +} X509_ACERT_ISSUER; + +typedef struct X509_holder_st { + OSSL_ISSUER_SERIAL *baseCertificateID; + STACK_OF(GENERAL_NAME) *entityName; + OSSL_OBJECT_DIGEST_INFO *objectDigestInfo; +} X509_HOLDER; + +struct X509_acert_info_st { + ASN1_INTEGER version; /* default of v2 */ + X509_HOLDER holder; + X509_ACERT_ISSUER issuer; + X509_ALGOR signature; + ASN1_INTEGER serialNumber; + X509_VAL validityPeriod; + STACK_OF(X509_ATTRIBUTE) *attributes; + ASN1_BIT_STRING *issuerUID; + X509_EXTENSIONS *extensions; +}; + +struct X509_acert_st { + X509_ACERT_INFO *acinfo; + X509_ALGOR sig_alg; + ASN1_BIT_STRING signature; +}; +#endif diff --git a/include/openssl/pem.h b/include/openssl/pem.h index 0446c77019ab8..6ea1a49a69445 100644 --- a/include/openssl/pem.h +++ b/include/openssl/pem.h @@ -58,6 +58,7 @@ extern "C" { # define PEM_STRING_PARAMETERS "PARAMETERS" # define PEM_STRING_CMS "CMS" # define PEM_STRING_SM2PARAMETERS "SM2 PARAMETERS" +# define PEM_STRING_ACERT "ATTRIBUTE CERTIFICATE" # define PEM_TYPE_ENCRYPTED 10 # define PEM_TYPE_MIC_ONLY 20 diff --git a/include/openssl/x509_acert.h.in b/include/openssl/x509_acert.h.in new file mode 100644 index 0000000000000..1d66af95457d3 --- /dev/null +++ b/include/openssl/x509_acert.h.in @@ -0,0 +1,48 @@ +/* + * {- join("\n * ", @autowarntext) -} + * + * Copyright 2022 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +{- +use OpenSSL::stackhash qw(generate_stack_macros); +-} + +#ifndef OPENSSL_X509_ACERT_H +# define OPENSSL_X509_ACERT_H +# pragma once + +# include +# include +# include + +typedef struct X509_acert_st X509_ACERT; +typedef struct X509_acert_info_st X509_ACERT_INFO; +typedef struct ossl_object_digest_info_st OSSL_OBJECT_DIGEST_INFO; +typedef struct ossl_issuer_serial_st OSSL_ISSUER_SERIAL; +typedef struct X509_acert_issuer_v2form_st X509_ACERT_ISSUER_V2FORM; + +DECLARE_ASN1_FUNCTIONS(X509_ACERT) +DECLARE_ASN1_DUP_FUNCTION(X509_ACERT) +DECLARE_ASN1_ITEM(X509_ACERT_INFO) +DECLARE_ASN1_ALLOC_FUNCTIONS(X509_ACERT_INFO) +DECLARE_ASN1_ALLOC_FUNCTIONS(OSSL_OBJECT_DIGEST_INFO) +DECLARE_ASN1_ALLOC_FUNCTIONS(OSSL_ISSUER_SERIAL) +DECLARE_ASN1_ALLOC_FUNCTIONS(X509_ACERT_ISSUER_V2FORM) + +# ifndef OPENSSL_NO_STDIO +X509_ACERT *d2i_X509_ACERT_fp(FILE *fp, X509_ACERT **acert); +int i2d_X509_ACERT_fp(FILE *fp, const X509_ACERT *acert); +# endif + +DECLARE_PEM_rw(X509_ACERT, X509_ACERT) + +X509_ACERT *d2i_X509_ACERT_bio(BIO *bp, X509_ACERT **acert); +int i2d_X509_ACERT_bio(BIO *bp, const X509_ACERT *acert); + +#endif diff --git a/util/libcrypto.num b/util/libcrypto.num index 75813690dcefe..91df653099e13 100644 --- a/util/libcrypto.num +++ b/util/libcrypto.num @@ -5548,3 +5548,26 @@ X509_STORE_get1_objects 5675 3_3_0 EXIST::FUNCTION: OPENSSL_LH_set_thunks 5676 3_3_0 EXIST::FUNCTION: OPENSSL_LH_doall_arg_thunk 5677 3_3_0 EXIST::FUNCTION: OSSL_HTTP_REQ_CTX_set_max_response_hdr_lines 5678 3_3_0 EXIST::FUNCTION:HTTP +d2i_X509_ACERT ? 3_4_0 EXIST::FUNCTION: +i2d_X509_ACERT ? 3_4_0 EXIST::FUNCTION: +X509_ACERT_free ? 3_4_0 EXIST::FUNCTION: +X509_ACERT_new ? 3_4_0 EXIST::FUNCTION: +X509_ACERT_it ? 3_4_0 EXIST::FUNCTION: +X509_ACERT_dup ? 3_4_0 EXIST::FUNCTION: +X509_ACERT_INFO_it ? 3_4_0 EXIST::FUNCTION: +X509_ACERT_INFO_free ? 3_4_0 EXIST::FUNCTION: +X509_ACERT_INFO_new ? 3_4_0 EXIST::FUNCTION: +OSSL_OBJECT_DIGEST_INFO_free ? 3_4_0 EXIST::FUNCTION: +OSSL_OBJECT_DIGEST_INFO_new ? 3_4_0 EXIST::FUNCTION: +OSSL_ISSUER_SERIAL_free ? 3_4_0 EXIST::FUNCTION: +OSSL_ISSUER_SERIAL_new ? 3_4_0 EXIST::FUNCTION: +X509_ACERT_ISSUER_V2FORM_free ? 3_4_0 EXIST::FUNCTION: +X509_ACERT_ISSUER_V2FORM_new ? 3_4_0 EXIST::FUNCTION: +d2i_X509_ACERT_fp ? 3_4_0 EXIST::FUNCTION:STDIO +i2d_X509_ACERT_fp ? 3_4_0 EXIST::FUNCTION:STDIO +PEM_read_X509_ACERT ? 3_4_0 EXIST::FUNCTION:STDIO +PEM_write_X509_ACERT ? 3_4_0 EXIST::FUNCTION:STDIO +PEM_read_bio_X509_ACERT ? 3_4_0 EXIST::FUNCTION: +PEM_write_bio_X509_ACERT ? 3_4_0 EXIST::FUNCTION: +d2i_X509_ACERT_bio ? 3_4_0 EXIST::FUNCTION: +i2d_X509_ACERT_bio ? 3_4_0 EXIST::FUNCTION: From 4958119cc8296974faa44578f371df2e522b8db0 Mon Sep 17 00:00:00 2001 From: Damian Hobson-Garcia Date: Fri, 18 Jun 2021 23:37:18 +0900 Subject: [PATCH 02/12] Attribute certificate getter and setter API Only fields that are allowed by RFC 5755 are accessible through this API. Fields that are only supported in version 1 attribute certificates (e.g. the AttCertIssuer v1Form fields) are not implemented. --- crypto/x509/build.info | 2 +- crypto/x509/x509_acert.c | 110 +++++++++++ crypto/x509/x509aset.c | 177 ++++++++++++++++++ doc/build.info | 6 + .../X509_ACERT_get0_holder_baseCertId.pod | 122 ++++++++++++ doc/man3/X509_get0_notBefore.pod | 25 +++ doc/man3/X509_get0_signature.pod | 19 +- doc/man3/X509_get0_uids.pod | 18 +- doc/man3/X509_get_serialNumber.pod | 24 ++- doc/man3/X509_get_subject_name.pod | 19 +- doc/man3/X509_get_version.pod | 12 +- include/openssl/x509_acert.h.in | 51 +++++ util/libcrypto.num | 28 +++ 13 files changed, 601 insertions(+), 12 deletions(-) create mode 100644 crypto/x509/x509aset.c create mode 100644 doc/man3/X509_ACERT_get0_holder_baseCertId.pod diff --git a/crypto/x509/build.info b/crypto/x509/build.info index 2976b37e0fcdc..b580d2b4f8ce6 100644 --- a/crypto/x509/build.info +++ b/crypto/x509/build.info @@ -16,7 +16,7 @@ SOURCE[../../libcrypto]=\ pcy_cache.c pcy_node.c pcy_data.c pcy_map.c pcy_tree.c pcy_lib.c \ v3_asid.c v3_addr.c v3_tlsf.c v3_admis.c v3_no_rev_avail.c \ v3_soa_id.c v3_no_ass.c v3_group_ac.c v3_single_use.c v3_ind_iss.c \ - x509_acert.c + x509_acert.c x509aset.c IF[{- !$disabled{'deprecated-3.0'} -}] SOURCE[../../libcrypto]=x509type.c diff --git a/crypto/x509/x509_acert.c b/crypto/x509/x509_acert.c index 9a1c298d7a5ac..d6b78bfc7cad3 100644 --- a/crypto/x509/x509_acert.c +++ b/crypto/x509/x509_acert.c @@ -73,3 +73,113 @@ IMPLEMENT_ASN1_ALLOC_FUNCTIONS(X509_ACERT_ISSUER_V2FORM) IMPLEMENT_PEM_rw(X509_ACERT, X509_ACERT, PEM_STRING_ACERT, X509_ACERT) +static X509_NAME *get_dirName(const GENERAL_NAMES *names) +{ + GENERAL_NAME *dirName; + + if (sk_GENERAL_NAME_num(names) != 1) + return NULL; + + dirName = sk_GENERAL_NAME_value(names, 0); + if (dirName->type != GEN_DIRNAME) + return NULL; + + return dirName->d.directoryName; +} + +void OSSL_OBJECT_DIGEST_INFO_get0_digest(const OSSL_OBJECT_DIGEST_INFO *o, + int *digestedObjectType, + const X509_ALGOR **digestAlgorithm, + const ASN1_BIT_STRING **digest) +{ + if (digestedObjectType != NULL) + *digestedObjectType = ASN1_ENUMERATED_get(&o->digestedObjectType); + if (digestAlgorithm != NULL) + *digestAlgorithm = &o->digestAlgorithm; + if (digest != NULL) + *digest = &o->objectDigest; +} + +const X509_NAME *OSSL_ISSUER_SERIAL_get0_issuer(const OSSL_ISSUER_SERIAL *isss) +{ + return get_dirName(isss->issuer); +} + +const ASN1_INTEGER *OSSL_ISSUER_SERIAL_get0_serial(const OSSL_ISSUER_SERIAL *isss) +{ + return &isss->serial; +} + +const ASN1_BIT_STRING *OSSL_ISSUER_SERIAL_get0_issuerUID(const OSSL_ISSUER_SERIAL *isss) +{ + return isss->issuerUID; +} + +long X509_ACERT_get_version(const X509_ACERT *x) +{ + return ASN1_INTEGER_get(&x->acinfo->version); +} + +void X509_ACERT_get0_signature(const X509_ACERT *x, + const ASN1_BIT_STRING **psig, + const X509_ALGOR **palg) +{ + if (psig != NULL) + *psig = &x->signature; + if (palg != NULL) + *palg = &x->sig_alg; +} + +int X509_ACERT_get_signature_nid(const X509_ACERT *x) +{ + return OBJ_obj2nid(x->sig_alg.algorithm); +} + +const GENERAL_NAMES *X509_ACERT_get0_holder_entityName(const X509_ACERT *x) +{ + return x->acinfo->holder.entityName; +} + +const OSSL_ISSUER_SERIAL *X509_ACERT_get0_holder_baseCertId(const X509_ACERT *x) +{ + return x->acinfo->holder.baseCertificateID; +} + +const OSSL_OBJECT_DIGEST_INFO *X509_ACERT_get0_holder_digest(const X509_ACERT *x) +{ + return x->acinfo->holder.objectDigestInfo; +} + +const X509_NAME *X509_ACERT_get0_issuerName(const X509_ACERT *x) +{ + if (x->acinfo->issuer.type != X509_ACERT_ISSUER_V2 + || x->acinfo->issuer.u.v2Form == NULL) + return NULL; + + return get_dirName(x->acinfo->issuer.u.v2Form->issuerName); +} + +const ASN1_BIT_STRING *X509_ACERT_get0_issuerUID(const X509_ACERT *x) +{ + return x->acinfo->issuerUID; +} + +const X509_ALGOR *X509_ACERT_get0_info_sigalg(const X509_ACERT *x) +{ + return &x->acinfo->signature; +} + +const ASN1_INTEGER *X509_ACERT_get0_serialNumber(const X509_ACERT *x) +{ + return &x->acinfo->serialNumber; +} + +const ASN1_GENERALIZEDTIME *X509_ACERT_get0_notBefore(const X509_ACERT *x) +{ + return x->acinfo->validityPeriod.notBefore; +} + +const ASN1_GENERALIZEDTIME *X509_ACERT_get0_notAfter(const X509_ACERT *x) +{ + return x->acinfo->validityPeriod.notAfter; +} diff --git a/crypto/x509/x509aset.c b/crypto/x509/x509aset.c new file mode 100644 index 0000000000000..3d7e5fbb21dc8 --- /dev/null +++ b/crypto/x509/x509aset.c @@ -0,0 +1,177 @@ +/* + * Copyright 2021 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include +#include +#include +#include "x509_acert.h" + +static int replace_gentime(ASN1_STRING **dest, const ASN1_GENERALIZEDTIME *src) +{ + ASN1_STRING *s; + + if (src->type != V_ASN1_GENERALIZEDTIME) + return 0; + + if (*dest == src) + return 1; + + s = ASN1_STRING_dup(src); + if (s == NULL) { + ERR_raise(ERR_LIB_X509, ERR_R_ASN1_LIB); + return 0; + } + + ASN1_STRING_free(*dest); + *dest = s; + + return 1; +} + +static int replace_dirName(GENERAL_NAMES **names, const X509_NAME *dirName) +{ + GENERAL_NAME *gen_name = NULL; + STACK_OF(GENERAL_NAME) *new_names = NULL; + X509_NAME *name_copy; + + if ((name_copy = X509_NAME_dup(dirName)) == NULL) { + ERR_raise(ERR_LIB_X509, ERR_R_ASN1_LIB); + goto err; + } + + if ((new_names = sk_GENERAL_NAME_new_null()) == NULL) { + ERR_raise(ERR_LIB_X509, ERR_R_ASN1_LIB); + goto err; + } + + if ((gen_name = GENERAL_NAME_new()) == NULL) { + ERR_raise(ERR_LIB_X509, ERR_R_ASN1_LIB); + goto err; + } + + if (sk_GENERAL_NAME_push(new_names, gen_name) <= 0) { + ERR_raise(ERR_LIB_X509, ERR_R_CRYPTO_LIB); + goto err; + } + + GENERAL_NAME_set0_value(gen_name, GEN_DIRNAME, name_copy); + + GENERAL_NAMES_free(*names); + *names = new_names; + + return 1; + +err: + GENERAL_NAME_free(gen_name); + sk_GENERAL_NAME_free(new_names); + X509_NAME_free(name_copy); + return 0; +} + +int OSSL_OBJECT_DIGEST_INFO_set1_digest(OSSL_OBJECT_DIGEST_INFO *o, + int digestedObjectType, + X509_ALGOR *digestAlgorithm, + ASN1_BIT_STRING *digest) +{ + + if (ASN1_ENUMERATED_set(&o->digestedObjectType, digestedObjectType) <= 0) + return 0; + + if (X509_ALGOR_copy(&o->digestAlgorithm, digestAlgorithm) <= 0) + return 0; + + if (ASN1_STRING_copy(&o->objectDigest, digest) <= 0) + return 0; + + return 1; +} + +int OSSL_ISSUER_SERIAL_set1_issuer(OSSL_ISSUER_SERIAL *isss, + const X509_NAME *issuer) +{ + return replace_dirName(&isss->issuer, issuer); +} + +int OSSL_ISSUER_SERIAL_set1_serial(OSSL_ISSUER_SERIAL *isss, + const ASN1_INTEGER *serial) +{ + return ASN1_STRING_copy(&isss->serial, serial); +} + +int OSSL_ISSUER_SERIAL_set1_issuerUID(OSSL_ISSUER_SERIAL *isss, + const ASN1_BIT_STRING *uid) +{ + ASN1_BIT_STRING_free(isss->issuerUID); + isss->issuerUID = ASN1_STRING_dup(uid); + if (isss->issuerUID == NULL) { + ERR_raise(ERR_LIB_X509, ERR_R_ASN1_LIB); + return 0; + } + return 1; +} + +int X509_ACERT_set_version(X509_ACERT *x, long version) +{ + return ASN1_INTEGER_set(&x->acinfo->version, version); +} + +void X509_ACERT_set0_holder_entityName(X509_ACERT *x, GENERAL_NAMES *names) +{ + GENERAL_NAMES_free(x->acinfo->holder.entityName); + x->acinfo->holder.entityName = names; +} + +void X509_ACERT_set0_holder_baseCertId(X509_ACERT *x, + OSSL_ISSUER_SERIAL *isss) +{ + OSSL_ISSUER_SERIAL_free(x->acinfo->holder.baseCertificateID); + x->acinfo->holder.baseCertificateID = isss; +} + +void X509_ACERT_set0_holder_digest(X509_ACERT *x, + OSSL_OBJECT_DIGEST_INFO *dinfo) +{ + OSSL_OBJECT_DIGEST_INFO_free(x->acinfo->holder.objectDigestInfo); + x->acinfo->holder.objectDigestInfo = dinfo; +} + +int X509_ACERT_set1_issuerName(X509_ACERT *x, const X509_NAME *name) +{ + X509_ACERT_ISSUER_V2FORM *v2Form; + + v2Form = x->acinfo->issuer.u.v2Form; + + /* only v2Form is supported, so always create that version */ + if (v2Form == NULL) { + v2Form = X509_ACERT_ISSUER_V2FORM_new(); + if (v2Form == NULL) { + ERR_raise(ERR_LIB_X509, ERR_R_ASN1_LIB); + return 0; + } + x->acinfo->issuer.u.v2Form = v2Form; + x->acinfo->issuer.type = X509_ACERT_ISSUER_V2; + } + + return replace_dirName(&v2Form->issuerName, name); +} + +int X509_ACERT_set1_serialNumber(X509_ACERT *x, const ASN1_INTEGER *serial) +{ + return ASN1_STRING_copy(&x->acinfo->serialNumber, serial); +} + +int X509_ACERT_set1_notBefore(X509_ACERT *x, const ASN1_GENERALIZEDTIME *time) +{ + return replace_gentime(&x->acinfo->validityPeriod.notBefore, time); +} + +int X509_ACERT_set1_notAfter(X509_ACERT *x, const ASN1_GENERALIZEDTIME *time) +{ + return replace_gentime(&x->acinfo->validityPeriod.notAfter, time); +} diff --git a/doc/build.info b/doc/build.info index 326c4ea34bbf2..da0e7a637f7ba 100644 --- a/doc/build.info +++ b/doc/build.info @@ -2799,6 +2799,10 @@ DEPEND[html/man3/X509V3_set_ctx.html]=man3/X509V3_set_ctx.pod GENERATE[html/man3/X509V3_set_ctx.html]=man3/X509V3_set_ctx.pod DEPEND[man/man3/X509V3_set_ctx.3]=man3/X509V3_set_ctx.pod GENERATE[man/man3/X509V3_set_ctx.3]=man3/X509V3_set_ctx.pod +DEPEND[html/man3/X509_ACERT_get0_holder_baseCertId.html]=man3/X509_ACERT_get0_holder_baseCertId.pod +GENERATE[html/man3/X509_ACERT_get0_holder_baseCertId.html]=man3/X509_ACERT_get0_holder_baseCertId.pod +DEPEND[man/man3/X509_ACERT_get0_holder_baseCertId.3]=man3/X509_ACERT_get0_holder_baseCertId.pod +GENERATE[man/man3/X509_ACERT_get0_holder_baseCertId.3]=man3/X509_ACERT_get0_holder_baseCertId.pod DEPEND[html/man3/X509_ALGOR_dup.html]=man3/X509_ALGOR_dup.pod GENERATE[html/man3/X509_ALGOR_dup.html]=man3/X509_ALGOR_dup.pod DEPEND[man/man3/X509_ALGOR_dup.3]=man3/X509_ALGOR_dup.pod @@ -3631,6 +3635,7 @@ html/man3/UI_create_method.html \ html/man3/UI_new.html \ html/man3/X509V3_get_d2i.html \ html/man3/X509V3_set_ctx.html \ +html/man3/X509_ACERT_get0_holder_baseCertId.html \ html/man3/X509_ALGOR_dup.html \ html/man3/X509_ATTRIBUTE.html \ html/man3/X509_CRL_get0_by_serial.html \ @@ -4276,6 +4281,7 @@ man/man3/UI_create_method.3 \ man/man3/UI_new.3 \ man/man3/X509V3_get_d2i.3 \ man/man3/X509V3_set_ctx.3 \ +man/man3/X509_ACERT_get0_holder_baseCertId.3 \ man/man3/X509_ALGOR_dup.3 \ man/man3/X509_ATTRIBUTE.3 \ man/man3/X509_CRL_get0_by_serial.3 \ diff --git a/doc/man3/X509_ACERT_get0_holder_baseCertId.pod b/doc/man3/X509_ACERT_get0_holder_baseCertId.pod new file mode 100644 index 0000000000000..7741f286d3cd3 --- /dev/null +++ b/doc/man3/X509_ACERT_get0_holder_baseCertId.pod @@ -0,0 +1,122 @@ +=pod + +=head1 NAME + +X509_ACERT_get0_holder_baseCertId, +X509_ACERT_get0_holder_digest, +X509_ACERT_get0_holder_entityName, +X509_ACERT_set0_holder_baseCertId, +X509_ACERT_set0_holder_digest, +X509_ACERT_set0_holder_entityName, +OSSL_ISSUER_SERIAL_get0_issuer, +OSSL_ISSUER_SERIAL_get0_issuerUID, +OSSL_ISSUER_SERIAL_get0_serial, +OSSL_ISSUER_SERIAL_set1_issuer, +OSSL_ISSUER_SERIAL_set1_issuerUID, +OSSL_ISSUER_SERIAL_set1_serial, +OSSL_OBJECT_DIGEST_INFO_get0_digest, +OSSL_OBJECT_DIGEST_INFO_set1_digest - get and set Attribute Certificate holder fields + +=head1 SYNOPSIS + + #include + + const GENERAL_NAMES *X509_ACERT_get0_holder_entityName(const X509_ACERT *x); + OSSL_ISSUER_SERIAL *X509_ACERT_get0_holder_baseCertId(const X509_ACERT *x); + OSSL_OBJECT_DIGEST_INFO * X509_ACERT_get0_holder_digest(const X509_ACERT *x); + void X509_ACERT_set0_holder_entityName(X509_ACERT *x, GENERAL_NAMES *name); + void X509_ACERT_set0_holder_baseCertId(X509_ACERT *x, OSSL_ISSUER_SERIAL *isss); + void X509_ACERT_set0_holder_digest(X509_ACERT *x, + OSSL_OBJECT_DIGEST_INFO *dinfo); + + X509_NAME *OSSL_ISSUER_SERIAL_get0_issuer(OSSL_ISSUER_SERIAL *isss); + ASN1_INTEGER *OSSL_ISSUER_SERIAL_get0_serial(OSSL_ISSUER_SERIAL *isss); + ASN1_BIT_STRING *OSSL_ISSUER_SERIAL_get0_issuerUID(OSSL_ISSUER_SERIAL *isss); + int OSSL_ISSUER_SERIAL_set1_issuer(OSSL_ISSUER_SERIAL *isss, X509_NAME *issuer); + int OSSL_ISSUER_SERIAL_set1_serial(OSSL_ISSUER_SERIAL *isss, ASN1_INTEGER *serial); + int OSSL_ISSUER_SERIAL_set1_issuerUID(OSSL_ISSUER_SERIAL *isss, ASN1_BIT_STRING *uid); + + void OSSL_OBJECT_DIGEST_INFO_get0_digest(OSSL_OBJECT_DIGEST_INFO *o, + ASN1_ENUMERATED **digestedObjectType, + X509_ALGOR **digestAlgorithm, + ASN1_BIT_STRING **digest); + void OSSL_OBJECT_DIGEST_INFO_set1_digest(OSSL_OBJECT_DIGEST_INFO *o, + ASN1_ENUMERATED *digestedObjectType, + X509_ALGOR *digestAlgorithm, + ASN1_BIT_STRING *digest); + +=head1 DESCRIPTION + +These routines set and get the holder identity of an X509 attribute certificate. + +X509_ACERT_set0_holder_entityName() sets the identity as a B +I, X509_ACERT_set0_holder_baseCertId() sets the identity based on the +issuer and serial number of a certificate detailed in I and +X509_ACERT_set0_holder_digest() sets the holder entity based on digest +information I. Although RFC 5755 section 4.2.2 recommends that only +one of the above methods be used to set the holder identity for a given +attribute certificate I, setting multiple methods at the same time is +possible. It is up to the application to handle cases when conflicting +identity information is specified using different methods. + +Pointers to the internal structures describing the holder identity of +attribute certificate I can be retrieved with +X509_ACERT_get0_holder_entityName(), X509_ACERT_get0_holder_baseCertId(), and +X509_ACERT_get0_holder_digest(). + +A B object holds the subject name and UID of a certificate +issuer and a certificate's serial number. OSSL_ISSUER_SERIAL_set1_issuer(), +OSSL_ISSUER_SERIAL_set1_issuerUID(), and OSSL_ISSUER_SERIAL_set1_serial() +respectively copy these values into the B structure. +The application is responsible for freeing its own copy of these values after +use. OSSL_ISSUER_SERIAL_get0_issuer(), OSSL_ISSUER_SERIAL_get0_issuerUID(), +and OSSL_ISSUER_SERIAL_get0_serial() return pointers to these values in the object. + +An B object holds a digest of data to identify the +attribute certificate holder. OSSL_OBJECT_DIGEST_INFO_set1_digest() sets the +digest information of the object. The type of I information is given +by I and can be one of: + +=over 4 + +=item OSSL_OBJECT_DIGEST_INFO_PUBLIC_KEY + +Hash of a public key + +=item OSSL_OBJECT_DIGEST_INFO_PUBLIC_KEY_CERT + +Hash of a public key certificate + +=item OSSL_OBJECT_DIGEST_INFO_OTHER + +Hash of another object. See NOTES below. + +=back + +I indicates the algorithm used to compute I. + +=head1 RETURN VALUES + +All I/I routines return 1 for success and 0 for failure. +All I functions return a pointer to the object's inner structure. These +pointers must not be freed after use. + +=head1 NOTES + +Although the value of B is defined in RFC 5755, +its use is prohibited for conformant attribute certificates. + +=head1 HISTORY + +These functions were added in OpenSSL 3.4. + +=head1 COPYRIGHT + +Copyright 2023 The OpenSSL Project Authors. All Rights Reserved. + +Licensed under the Apache License 2.0 (the "License"). You may not use +this file except in compliance with the License. You can obtain a copy +in the file LICENSE in the source distribution or at +L. + +=cut diff --git a/doc/man3/X509_get0_notBefore.pod b/doc/man3/X509_get0_notBefore.pod index 1ca0a1b128aa8..84dee91850c2d 100644 --- a/doc/man3/X509_get0_notBefore.pod +++ b/doc/man3/X509_get0_notBefore.pod @@ -4,6 +4,8 @@ X509_get0_notBefore, X509_getm_notBefore, X509_get0_notAfter, X509_getm_notAfter, X509_set1_notBefore, X509_set1_notAfter, +X509_ACERT_get0_notBefore, X509_ACERT_get0_notAfter, +X509_ACERT_set1_notBefore, X509_ACERT_set1_notAfter, X509_CRL_get0_lastUpdate, X509_CRL_get0_nextUpdate, X509_CRL_set1_lastUpdate, X509_CRL_set1_nextUpdate - get or set certificate or CRL dates @@ -20,6 +22,12 @@ X509_CRL_set1_nextUpdate - get or set certificate or CRL dates int X509_set1_notBefore(X509 *x, const ASN1_TIME *tm); int X509_set1_notAfter(X509 *x, const ASN1_TIME *tm); + const ASN1_GENERALIZEDTIME *X509_ACERT_get0_notBefore(const X509 *x); + const ASN1_GENERALIZEDTIME *X509_ACERT_get0_notAfter(const X509 *x); + + int X509_ACERT_set1_notBefore(X509_ACERT *x, const ASN1_GENERALIZEDTIME *tm); + int X509_ACERT_set1_notAfter(X509_ACERT *x, const ASN1_GENERALIZEDTIME *tm); + const ASN1_TIME *X509_CRL_get0_lastUpdate(const X509_CRL *crl); const ASN1_TIME *X509_CRL_get0_nextUpdate(const X509_CRL *crl); @@ -43,6 +51,16 @@ and B fields of I to I. Ownership of the passed parameter I is not transferred by these functions so it must be freed up after the call. +X509_ACERT_get0_notBefore() and X509_ACERT_get0_notAfter() return +the B and B fields of certificate B respectively. +returned is an internal pointer which must not be freed up after +the call. + +X509_ACERT_set1_notBefore() and X509_ACERT_set1_notAfter() set the B +and B fields of B to B. Ownership of the passed +parameter B is not transferred by these functions so it must +be freed up after the call. + X509_CRL_get0_lastUpdate() and X509_CRL_get0_nextUpdate() return the B and B fields of I. The value returned is an internal pointer which must not be freed up after @@ -67,9 +85,16 @@ or NULL if the B field is absent. X509_set1_notBefore(), X509_set1_notAfter(), X509_CRL_set1_lastUpdate() and X509_CRL_set1_nextUpdate() return 1 for success or 0 for failure. +=head1 NOTES + +Unlike the B and B routines, the B routines +use the ASN1_GENERALIZEDTIME format instead of ASN1_TIME for holding time +data. + =head1 SEE ALSO L, +L L, L, L, diff --git a/doc/man3/X509_get0_signature.pod b/doc/man3/X509_get0_signature.pod index a49a70038e31f..485d39bf25125 100644 --- a/doc/man3/X509_get0_signature.pod +++ b/doc/man3/X509_get0_signature.pod @@ -5,7 +5,9 @@ X509_get0_signature, X509_REQ_set0_signature, X509_REQ_set1_signature_algo, X509_get_signature_nid, X509_get0_tbs_sigalg, X509_REQ_get0_signature, X509_REQ_get_signature_nid, X509_CRL_get0_signature, X509_CRL_get_signature_nid, -X509_get_signature_info, X509_SIG_INFO_get, X509_SIG_INFO_set - signature information +X509_ACERT_get0_signature, X509_ACERT_get0_info_sigalg, +X509_ACERT_get_signature_nid, X509_get_signature_info, +X509_SIG_INFO_get, X509_SIG_INFO_set - signature information =head1 SYNOPSIS @@ -24,6 +26,8 @@ X509_get_signature_info, X509_SIG_INFO_get, X509_SIG_INFO_set - signature inform const X509_ALGOR **palg); int X509_REQ_get_signature_nid(const X509_REQ *crl); + const X509_ALGOR *X509_ACERT_get0_info_sigalg(const X509_ACERT *x); + void X509_CRL_get0_signature(const X509_CRL *crl, const ASN1_BIT_STRING **psig, const X509_ALGOR **palg); @@ -37,6 +41,12 @@ X509_get_signature_info, X509_SIG_INFO_get, X509_SIG_INFO_set - signature inform void X509_SIG_INFO_set(X509_SIG_INFO *siginf, int mdnid, int pknid, int secbits, uint32_t flags); + #include + + void X509_ACERT_get0_signature(const X509_ACERT *x, + const ASN1_BIT_STRING **psig, + const X509_ALGOR **palg); + int X509_ACERT_get_signature_nid(const X509_ACERT *x); =head1 DESCRIPTION X509_get0_signature() sets B<*psig> to the signature of B and B<*palg> @@ -56,6 +66,10 @@ X509_REQ_get0_signature(), X509_REQ_get_signature_nid() X509_CRL_get0_signature() and X509_CRL_get_signature_nid() perform the same function for certificate requests and CRLs. +X509_ACERT_get0_signature(), X509_ACERT_get_signature_nid() and +X509_ACERT_get0_info_sigalg() perform the same function for attribute +certificates. + X509_get_signature_info() retrieves information about the signature of certificate B. The NID of the signing digest is written to B<*mdnid>, the public key algorithm to B<*pknid>, the effective security bits to @@ -130,6 +144,9 @@ added in OpenSSL 1.1.0. The X509_REQ_set0_signature() and X509_REQ_set1_signature_algo() were added in OpenSSL 1.1.1e. +The X509_ACERT_get0_signature(), X509_ACERT_get0_info_sigalg() and +X509_ACERT_get_signature_nid() functions were added in OpenSSL 3.4. + =head1 COPYRIGHT Copyright 2015-2020 The OpenSSL Project Authors. All Rights Reserved. diff --git a/doc/man3/X509_get0_uids.pod b/doc/man3/X509_get0_uids.pod index 48ae5afc4243a..c265ce09bcff4 100644 --- a/doc/man3/X509_get0_uids.pod +++ b/doc/man3/X509_get0_uids.pod @@ -2,7 +2,8 @@ =head1 NAME -X509_get0_uids - get certificate unique identifiers +X509_get0_uids, X509_ACERT_get0_issuerUID +- get certificate and attribute certificate unique identifiers =head1 SYNOPSIS @@ -11,11 +12,17 @@ X509_get0_uids - get certificate unique identifiers void X509_get0_uids(const X509 *x, const ASN1_BIT_STRING **piuid, const ASN1_BIT_STRING **psuid); + #include + + ASN1_BIT_STRING *X509_ACERT_get0_issuerUID(X509_ACERT *x); =head1 DESCRIPTION X509_get0_uids() sets B<*piuid> and B<*psuid> to the issuer and subject unique identifiers of certificate B or NULL if the fields are not present. +X509_ACERT_get0_issuerUID() returns the issuer unique identifier of the +attribute certificate B or NULL if the field is not present. + =head1 NOTES The issuer and subject unique identifier fields are very rarely encountered in @@ -25,6 +32,9 @@ practice outside test cases. X509_get0_uids() does not return a value. +X509_ACERT_get0_issuerUID() returns a unique identifier on success or NULL +on failure. + =head1 SEE ALSO L, @@ -45,6 +55,12 @@ L, L, L +=head1 HISTORY + +X509_get0_uids() was added in OpenSSL 1.1.0. + +X509_ACERT_get0_issuerUID() was added in OpenSSL 3.4. + =head1 COPYRIGHT Copyright 2015-2016 The OpenSSL Project Authors. All Rights Reserved. diff --git a/doc/man3/X509_get_serialNumber.pod b/doc/man3/X509_get_serialNumber.pod index 5594c8a284b06..ac93a33b8f266 100644 --- a/doc/man3/X509_get_serialNumber.pod +++ b/doc/man3/X509_get_serialNumber.pod @@ -4,7 +4,9 @@ X509_get_serialNumber, X509_get0_serialNumber, -X509_set_serialNumber +X509_set_serialNumber, +X509_ACERT_get0_serialNumber, +X509_ACERT_set1_serialNumber - get or set certificate serial number =head1 SYNOPSIS @@ -15,6 +17,11 @@ X509_set_serialNumber const ASN1_INTEGER *X509_get0_serialNumber(const X509 *x); int X509_set_serialNumber(X509 *x, ASN1_INTEGER *serial); + #include + + ASN1_INTEGER *X509_ACERT_get0_serialNumber(X509_ACERT *x); + int X509_ACERT_set1_serialNumber(X509_ACERT *x, ASN1_INTEGER *serial); + =head1 DESCRIPTION X509_get_serialNumber() returns the serial number of certificate B as an @@ -28,12 +35,19 @@ X509_set_serialNumber() sets the serial number of certificate B to B. A copy of the serial number is used internally so B should be freed up after use. +X509_ACERT_get0_serialNumber() performs the same operation as +X509_get_serialNumber() for attribute certificates. + +X509_ACERT_set1_serialNumber() performs the same operation as +X509_set_serialNumber() for attribute certificates. + =head1 RETURN VALUES -X509_get_serialNumber() and X509_get0_serialNumber() return an B -structure. +X509_get_serialNumber(), X509_get0_serialNumber() and +X509_ACERT_get0_serialNumber() return a pointer to an B structure. -X509_set_serialNumber() returns 1 for success and 0 for failure. +X509_set_serialNumber() and X509_ACERT_set1_serialNumber() return 1 for success +and 0 for failure. =head1 SEE ALSO @@ -59,6 +73,8 @@ L The X509_get_serialNumber() and X509_set_serialNumber() functions are available in all versions of OpenSSL. The X509_get0_serialNumber() function was added in OpenSSL 1.1.0. +The X509_ACERT_get0_serialNumber() and X509_ACERT_set1_serialNumber() +functions were added in OpenSSL 3.4. =head1 COPYRIGHT diff --git a/doc/man3/X509_get_subject_name.pod b/doc/man3/X509_get_subject_name.pod index 64659de6ab6a7..2f392ad7a2e6c 100644 --- a/doc/man3/X509_get_subject_name.pod +++ b/doc/man3/X509_get_subject_name.pod @@ -6,6 +6,7 @@ X509_NAME_hash_ex, X509_NAME_hash, X509_get_subject_name, X509_set_subject_name, X509_subject_name_hash, X509_get_issuer_name, X509_set_issuer_name, X509_issuer_name_hash, X509_REQ_get_subject_name, X509_REQ_set_subject_name, +X509_ACERT_get0_issuerName, X509_ACERT_set1_issuerName, X509_CRL_get_issuer, X509_CRL_set_issuer_name - get X509_NAME hashes or get and set issuer or subject names @@ -30,6 +31,11 @@ get X509_NAME hashes or get and set issuer or subject names X509_NAME *X509_CRL_get_issuer(const X509_CRL *crl); int X509_CRL_set_issuer_name(X509_CRL *x, const X509_NAME *name); + #include + + X509_NAME *X509_ACERT_get0_issuerName(const X509_ACERT *x); + int X509_ACERT_set1_issuerName(X509_ACERT *x, const X509_NAME *name); + The following macro has been deprecated since OpenSSL 3.0, and can be hidden entirely by defining B with a suitable version value, see L: @@ -63,13 +69,19 @@ X509_get_subject_name(), X509_set_subject_name(), and X509_subject_name_hash() except they relate to the issuer name of I. Similarly X509_REQ_get_subject_name(), X509_REQ_set_subject_name(), +X509_ACERT_get0_issuerName(), X509_ACERT_set1_issuerName(), X509_CRL_get_issuer() and X509_CRL_set_issuer_name() get or set the subject or issuer names of certificate requests of CRLs respectively. +Since attribute certificates do not have a subject name, only the issuer name +can be set. For details on setting X509_ACERT holder identities, see +L. + =head1 RETURN VALUES X509_get_subject_name(), X509_get_issuer_name(), X509_REQ_get_subject_name() -and X509_CRL_get_issuer() return an B pointer. +X509_ACERT_get0_issuerName() and X509_CRL_get_issuer() return +an B pointer. X509_NAME_hash_ex(), X509_NAME_hash(), X509_subject_name_hash() and X509_issuer_name_hash() @@ -77,8 +89,9 @@ return the first four bytes of the SHA1 hash value, converted to B in little endian order, or 0 on failure. -X509_set_subject_name(), X509_set_issuer_name(), X509_REQ_set_subject_name() -and X509_CRL_set_issuer_name() return 1 for success and 0 for failure. +X509_set_subject_name(), X509_set_issuer_name(), X509_REQ_set_subject_name(), +X509_ACERT_get0_issuerName() and X509_CRL_set_issuer_name() return 1 for +success and 0 for failure. =head1 BUGS diff --git a/doc/man3/X509_get_version.pod b/doc/man3/X509_get_version.pod index c5db26c5790ce..d6b11e454fd35 100644 --- a/doc/man3/X509_get_version.pod +++ b/doc/man3/X509_get_version.pod @@ -3,7 +3,8 @@ =head1 NAME X509_get_version, X509_set_version, X509_REQ_get_version, X509_REQ_set_version, -X509_CRL_get_version, X509_CRL_set_version - get or set certificate, +X509_ACERT_get_version, X509_ACERT_set_version, X509_CRL_get_version, +X509_CRL_set_version - get or set certificate, certificate request or CRL version =head1 SYNOPSIS @@ -19,6 +20,11 @@ certificate request or CRL version long X509_CRL_get_version(const X509_CRL *crl); int X509_CRL_set_version(X509_CRL *x, long version); + #include + + int X509_ACERT_set_version(X509_ACERT *x, long version); + long X509_ACERT_get_version(const X509_ACERT *x); + =head1 DESCRIPTION X509_get_version() returns the numerical value of the version field of @@ -31,9 +37,11 @@ X509_set_version() sets the numerical value of the version field of certificate I to I. Similarly X509_REQ_get_version(), X509_REQ_set_version(), +X509_ACERT_get_version(), X509_ACERT_set_version(), X509_CRL_get_version() and X509_CRL_set_version() get and set the version number of certificate requests and CRLs. They use constants -B, B, and B. +B, B, B, +and B. =head1 NOTES diff --git a/include/openssl/x509_acert.h.in b/include/openssl/x509_acert.h.in index 1d66af95457d3..826f39d423c53 100644 --- a/include/openssl/x509_acert.h.in +++ b/include/openssl/x509_acert.h.in @@ -45,4 +45,55 @@ DECLARE_PEM_rw(X509_ACERT, X509_ACERT) X509_ACERT *d2i_X509_ACERT_bio(BIO *bp, X509_ACERT **acert); int i2d_X509_ACERT_bio(BIO *bp, const X509_ACERT *acert); +# define X509_ACERT_VERSION_2 1 + +const GENERAL_NAMES *X509_ACERT_get0_holder_entityName(const X509_ACERT *x); +const OSSL_ISSUER_SERIAL *X509_ACERT_get0_holder_baseCertId(const X509_ACERT *x); +const OSSL_OBJECT_DIGEST_INFO * X509_ACERT_get0_holder_digest(const X509_ACERT *x); +const X509_NAME *X509_ACERT_get0_issuerName(const X509_ACERT *x); +long X509_ACERT_get_version(const X509_ACERT *x); +void X509_ACERT_get0_signature(const X509_ACERT *x, + const ASN1_BIT_STRING **psig, + const X509_ALGOR **palg); +int X509_ACERT_get_signature_nid(const X509_ACERT *x); +const X509_ALGOR *X509_ACERT_get0_info_sigalg(const X509_ACERT *x); +const ASN1_INTEGER *X509_ACERT_get0_serialNumber(const X509_ACERT *x); +const ASN1_TIME *X509_ACERT_get0_notBefore(const X509_ACERT *x); +const ASN1_TIME *X509_ACERT_get0_notAfter(const X509_ACERT *x); +const ASN1_BIT_STRING *X509_ACERT_get0_issuerUID(const X509_ACERT *x); + +# define OSSL_OBJECT_DIGEST_INFO_PUBLIC_KEY 0 +# define OSSL_OBJECT_DIGEST_INFO_PUBLIC_KEY_CERT 1 +# define OSSL_OBJECT_DIGEST_INFO_OTHER 2 /* must not be used in RFC 5755 profile */ +int X509_ACERT_set_version(X509_ACERT *x, long version); +void X509_ACERT_set0_holder_entityName(X509_ACERT *x, GENERAL_NAMES *name); +void X509_ACERT_set0_holder_baseCertId(X509_ACERT *x, OSSL_ISSUER_SERIAL *isss); +void X509_ACERT_set0_holder_digest(X509_ACERT *x, + OSSL_OBJECT_DIGEST_INFO *dinfo); + +int X509_ACERT_set1_issuerName(X509_ACERT *x, const X509_NAME *name); +int X509_ACERT_set1_serialNumber(X509_ACERT *x, const ASN1_INTEGER *serial); +int X509_ACERT_set1_notBefore(X509_ACERT *x, const ASN1_GENERALIZEDTIME *time); +int X509_ACERT_set1_notAfter(X509_ACERT *x, const ASN1_GENERALIZEDTIME *time); + +void OSSL_OBJECT_DIGEST_INFO_get0_digest(const OSSL_OBJECT_DIGEST_INFO *o, + int *digestedObjectType, + const X509_ALGOR **digestAlgorithm, + const ASN1_BIT_STRING **digest); + +int OSSL_OBJECT_DIGEST_INFO_set1_digest(OSSL_OBJECT_DIGEST_INFO *o, + int digestedObjectType, + X509_ALGOR *digestAlgorithm, + ASN1_BIT_STRING *digest); + +const X509_NAME *OSSL_ISSUER_SERIAL_get0_issuer(const OSSL_ISSUER_SERIAL *isss); +const ASN1_INTEGER *OSSL_ISSUER_SERIAL_get0_serial(const OSSL_ISSUER_SERIAL *isss); +const ASN1_BIT_STRING *OSSL_ISSUER_SERIAL_get0_issuerUID(const OSSL_ISSUER_SERIAL *isss); + +int OSSL_ISSUER_SERIAL_set1_issuer(OSSL_ISSUER_SERIAL *isss, + const X509_NAME *issuer); +int OSSL_ISSUER_SERIAL_set1_serial(OSSL_ISSUER_SERIAL *isss, + const ASN1_INTEGER *serial); +int OSSL_ISSUER_SERIAL_set1_issuerUID(OSSL_ISSUER_SERIAL *isss, + const ASN1_BIT_STRING *uid); #endif diff --git a/util/libcrypto.num b/util/libcrypto.num index 91df653099e13..082de0db1f29d 100644 --- a/util/libcrypto.num +++ b/util/libcrypto.num @@ -5571,3 +5571,31 @@ PEM_read_bio_X509_ACERT ? 3_4_0 EXIST::FUNCTION: PEM_write_bio_X509_ACERT ? 3_4_0 EXIST::FUNCTION: d2i_X509_ACERT_bio ? 3_4_0 EXIST::FUNCTION: i2d_X509_ACERT_bio ? 3_4_0 EXIST::FUNCTION: +X509_ACERT_get0_holder_entityName ? 3_4_0 EXIST::FUNCTION: +X509_ACERT_get0_holder_baseCertId ? 3_4_0 EXIST::FUNCTION: +X509_ACERT_get0_holder_digest ? 3_4_0 EXIST::FUNCTION: +X509_ACERT_get0_issuerName ? 3_4_0 EXIST::FUNCTION: +X509_ACERT_get_version ? 3_4_0 EXIST::FUNCTION: +X509_ACERT_get0_signature ? 3_4_0 EXIST::FUNCTION: +X509_ACERT_get_signature_nid ? 3_4_0 EXIST::FUNCTION: +X509_ACERT_get0_info_sigalg ? 3_4_0 EXIST::FUNCTION: +X509_ACERT_get0_serialNumber ? 3_4_0 EXIST::FUNCTION: +X509_ACERT_get0_notBefore ? 3_4_0 EXIST::FUNCTION: +X509_ACERT_get0_notAfter ? 3_4_0 EXIST::FUNCTION: +X509_ACERT_get0_issuerUID ? 3_4_0 EXIST::FUNCTION: +X509_ACERT_set_version ? 3_4_0 EXIST::FUNCTION: +X509_ACERT_set0_holder_entityName ? 3_4_0 EXIST::FUNCTION: +X509_ACERT_set0_holder_baseCertId ? 3_4_0 EXIST::FUNCTION: +X509_ACERT_set0_holder_digest ? 3_4_0 EXIST::FUNCTION: +X509_ACERT_set1_issuerName ? 3_4_0 EXIST::FUNCTION: +X509_ACERT_set1_serialNumber ? 3_4_0 EXIST::FUNCTION: +X509_ACERT_set1_notBefore ? 3_4_0 EXIST::FUNCTION: +X509_ACERT_set1_notAfter ? 3_4_0 EXIST::FUNCTION: +OSSL_OBJECT_DIGEST_INFO_get0_digest ? 3_4_0 EXIST::FUNCTION: +OSSL_OBJECT_DIGEST_INFO_set1_digest ? 3_4_0 EXIST::FUNCTION: +OSSL_ISSUER_SERIAL_get0_issuer ? 3_4_0 EXIST::FUNCTION: +OSSL_ISSUER_SERIAL_get0_serial ? 3_4_0 EXIST::FUNCTION: +OSSL_ISSUER_SERIAL_get0_issuerUID ? 3_4_0 EXIST::FUNCTION: +OSSL_ISSUER_SERIAL_set1_issuer ? 3_4_0 EXIST::FUNCTION: +OSSL_ISSUER_SERIAL_set1_serial ? 3_4_0 EXIST::FUNCTION: +OSSL_ISSUER_SERIAL_set1_issuerUID ? 3_4_0 EXIST::FUNCTION: From 1d30273bad6361cf9cedcbed22628ffb978bcddd Mon Sep 17 00:00:00 2001 From: Damian Hobson-Garcia Date: Fri, 30 Jun 2023 17:44:29 -0400 Subject: [PATCH 03/12] Attribute certificate printing functions Add functions to print an attribute certificate. Several attribute value types defined by the RFC 5755 specification are multi-field values (i.e ASN1_SEQUENCE rather than an ASN1_STRING or similar format). Currently those values are printed using `ASN1_item_print`. A more user-friendly output mechanism (maybe similar to the i2r_ functions used for X509 extensions) could be added in future. --- crypto/x509/build.info | 2 +- crypto/x509/t_acert.c | 285 +++++++++++++++++++++++++++++++ doc/build.info | 6 + doc/man3/X509_ACERT_print_ex.pod | 112 ++++++++++++ include/openssl/x509_acert.h.in | 4 + util/libcrypto.num | 2 + 6 files changed, 410 insertions(+), 1 deletion(-) create mode 100644 crypto/x509/t_acert.c create mode 100644 doc/man3/X509_ACERT_print_ex.pod diff --git a/crypto/x509/build.info b/crypto/x509/build.info index b580d2b4f8ce6..873a9838b5dd6 100644 --- a/crypto/x509/build.info +++ b/crypto/x509/build.info @@ -16,7 +16,7 @@ SOURCE[../../libcrypto]=\ pcy_cache.c pcy_node.c pcy_data.c pcy_map.c pcy_tree.c pcy_lib.c \ v3_asid.c v3_addr.c v3_tlsf.c v3_admis.c v3_no_rev_avail.c \ v3_soa_id.c v3_no_ass.c v3_group_ac.c v3_single_use.c v3_ind_iss.c \ - x509_acert.c x509aset.c + x509_acert.c x509aset.c t_acert.c IF[{- !$disabled{'deprecated-3.0'} -}] SOURCE[../../libcrypto]=x509type.c diff --git a/crypto/x509/t_acert.c b/crypto/x509/t_acert.c new file mode 100644 index 0000000000000..0b5ab313115cd --- /dev/null +++ b/crypto/x509/t_acert.c @@ -0,0 +1,285 @@ +/* + * Copyright 2021 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include +#include "internal/cryptlib.h" +#include +#include +#include +#include + +static int print_attribute(BIO *bp, X509_ATTRIBUTE *a) +{ + ASN1_OBJECT *aobj; + int i, j, count; + int ret = 0; + + aobj = X509_ATTRIBUTE_get0_object(a); + if (BIO_printf(bp, "%12s", "") <= 0) + goto err; + + if ((j = i2a_ASN1_OBJECT(bp, aobj)) <= 0) + goto err; + + count = X509_ATTRIBUTE_count(a); + if (count == 0) { + ERR_raise(ERR_LIB_X509, X509_R_INVALID_ATTRIBUTES); + goto err; + } + + if (j < 25 && (BIO_printf(bp, "%*s", 25 - j, " ") <= 0)) + goto err; + + if (BIO_puts(bp, ":") <= 0) + goto err; + + for (i = 0; i < count; i++) { + ASN1_TYPE *at; + int type; + ASN1_BIT_STRING *bs; + + at = X509_ATTRIBUTE_get0_type(a, i); + type = at->type; + + switch (type) { + case V_ASN1_PRINTABLESTRING: + case V_ASN1_T61STRING: + case V_ASN1_NUMERICSTRING: + case V_ASN1_UTF8STRING: + case V_ASN1_IA5STRING: + bs = at->value.asn1_string; + if (BIO_write(bp, (char *)bs->data, bs->length) != bs->length) + goto err; + if (BIO_puts(bp, "\n") <= 0) + goto err; + break; + case V_ASN1_SEQUENCE: + if (BIO_puts(bp, "\n") <= 0) + goto err; + ASN1_parse_dump(bp, at->value.sequence->data, + at->value.sequence->length, i, 1); + break; + default: + if (BIO_printf(bp, "unable to print attribute of type 0x%X\n", + type) < 0) + goto err; + break; + } + } + ret = 1; +err: + return ret; +} + +int X509_ACERT_print_ex(BIO *bp, X509_ACERT *x, unsigned long nmflags, + unsigned long cflag) +{ + int i; + char mlch = ' '; + + if ((nmflags & XN_FLAG_SEP_MASK) == XN_FLAG_SEP_MULTILINE) { + mlch = '\n'; + } + + if ((cflag & X509_FLAG_NO_HEADER) == 0) { + if (BIO_printf(bp, "Attribute Certificate:\n") <= 0) + goto err; + if (BIO_printf(bp, "%4sData:\n", "") <= 0) + goto err; + } + + if ((cflag & X509_FLAG_NO_VERSION) == 0) { + long l; + + l = X509_ACERT_get_version(x); + if (l == X509_ACERT_VERSION_2) { + if (BIO_printf(bp, "%8sVersion: %ld (0x%lx)\n", "", l + 1, + (unsigned long)l) <= 0) + goto err; + } else { + if (BIO_printf(bp, "%8sVersion: Unknown (%ld)\n", "", l) <= 0) + goto err; + } + } + + if ((cflag & X509_FLAG_NO_SERIAL) == 0) { + const ASN1_INTEGER *serial; + + serial = X509_ACERT_get0_serialNumber(x); + + if (BIO_printf(bp, "%8sSerial Number: ", "") <= 0) + goto err; + + if (i2a_ASN1_INTEGER(bp, serial) <= 0) + goto err; + + if (BIO_write(bp, "\n", 1) <= 0) + goto err; + } + + if ((cflag & X509_FLAG_NO_SUBJECT) == 0) { + const GENERAL_NAMES *holderEntities; + const OSSL_ISSUER_SERIAL *holder_bcid; + const X509_NAME *holderIssuer = NULL; + + if (BIO_printf(bp, "%8sHolder:\n", "") <= 0) + goto err; + + holderEntities = X509_ACERT_get0_holder_entityName(x); + if (holderEntities != NULL) { + for (i = 0; i < sk_GENERAL_NAME_num(holderEntities); i++) { + GENERAL_NAME *entity; + + entity = sk_GENERAL_NAME_value(holderEntities, i); + + if (BIO_printf(bp, "%12sName:%c", "", mlch) <= 0) + goto err; + if (GENERAL_NAME_print(bp, entity) <= 0) + goto err; + if (BIO_write(bp, "\n", 1) <= 0) + goto err; + } + } + + if ((holder_bcid = X509_ACERT_get0_holder_baseCertId(x)) != NULL) + holderIssuer = OSSL_ISSUER_SERIAL_get0_issuer(holder_bcid); + + if (holderIssuer != NULL) { + const ASN1_INTEGER *holder_serial; + const ASN1_BIT_STRING *iuid; + + if (BIO_printf(bp, "%12sIssuer:%c", "", mlch) <= 0) + goto err; + + if (X509_NAME_print_ex(bp, holderIssuer, 0, nmflags) <= 0) + goto err; + + if (BIO_write(bp, "\n", 1) <= 0) + goto err; + + if (BIO_printf(bp, "%12sSerial: ", "") <= 0) + goto err; + + holder_serial = OSSL_ISSUER_SERIAL_get0_serial(holder_bcid); + + if (i2a_ASN1_INTEGER(bp, holder_serial) <= 0) + goto err; + + iuid = OSSL_ISSUER_SERIAL_get0_issuerUID(holder_bcid); + if (iuid != NULL) { + if (BIO_printf(bp, "%12sIssuer UID: ", "") <= 0) + goto err; + if (X509_signature_dump(bp, iuid, 24) <= 0) + goto err; + } + if (BIO_write(bp, "\n", 1) <= 0) + goto err; + } + } + + if ((cflag & X509_FLAG_NO_ISSUER) == 0) { + const X509_NAME *issuer; + + if (BIO_printf(bp, "%8sIssuer:%c", "", mlch) <= 0) + goto err; + issuer = X509_ACERT_get0_issuerName(x); + if (issuer) { + if (X509_NAME_print_ex(bp, issuer, 0, nmflags) < 0) + goto err; + } else { + if (BIO_printf(bp, "Unsupported Issuer Type") <= 0) + goto err; + } + if (BIO_write(bp, "\n", 1) <= 0) + goto err; + } + + if ((cflag & X509_FLAG_NO_VALIDITY) == 0) { + if (BIO_printf(bp, "%8sValidity\n", "") <= 0) + goto err; + if (BIO_printf(bp, "%12sNot Before: ", "") <= 0) + goto err; + if (ASN1_GENERALIZEDTIME_print(bp, X509_ACERT_get0_notBefore(x)) == 0) + goto err; + if (BIO_printf(bp, "\n%12sNot After : ", "") <= 0) + goto err; + if (ASN1_GENERALIZEDTIME_print(bp, X509_ACERT_get0_notAfter(x)) == 0) + goto err; + if (BIO_write(bp, "\n", 1) <= 0) + goto err; + } + + if ((cflag & X509_FLAG_NO_ATTRIBUTES) == 0) { + if (BIO_printf(bp, "%8sAttributes:\n", "") <= 0) + goto err; + + if (X509_ACERT_get_attr_count(x) == 0) { + if (BIO_printf(bp, "%12s(none)\n", "") <= 0) + goto err; + } else { + for (i = 0; i < X509_ACERT_get_attr_count(x); i++) { + if (print_attribute(bp, X509_ACERT_get_attr(x, i)) == 0) + goto err; + } + } + } + + if ((cflag & X509_FLAG_NO_EXTENSIONS) == 0) { + const STACK_OF(X509_EXTENSION) *exts; + + exts = X509_ACERT_get0_extensions(x); + if (exts != NULL) { + if (BIO_printf(bp, "%8sExtensions:\n", "") <= 0) + goto err; + for (i = 0; i < sk_X509_EXTENSION_num(exts); i++) { + ASN1_OBJECT *obj; + X509_EXTENSION *ex; + int critical; + + ex = sk_X509_EXTENSION_value(exts, i); + if (BIO_printf(bp, "%12s", "") <= 0) + goto err; + obj = X509_EXTENSION_get_object(ex); + if (i2a_ASN1_OBJECT(bp, obj) <= 0) + goto err; + critical = X509_EXTENSION_get_critical(ex); + if (BIO_printf(bp, ": %s\n", critical ? "critical" : "") <= 0) + goto err; + if (X509V3_EXT_print(bp, ex, cflag, 20) <= 0) { + if (BIO_printf(bp, "%16s", "") <= 0) + goto err; + if (ASN1_STRING_print(bp, X509_EXTENSION_get_data(ex)) <= 0) + goto err; + } + if (BIO_write(bp, "\n", 1) <= 0) + goto err; + } + } + } + + if ((cflag & X509_FLAG_NO_SIGDUMP) == 0) { + const X509_ALGOR *sig_alg; + const ASN1_BIT_STRING *sig; + + X509_ACERT_get0_signature(x, &sig, &sig_alg); + if (X509_signature_print(bp, sig_alg, sig) <= 0) + return 0; + } + + return 1; + +err: + ERR_raise(ERR_LIB_X509, ERR_R_BUF_LIB); + return 0; +} + +int X509_ACERT_print(BIO *bp, X509_ACERT *x) +{ + return X509_ACERT_print_ex(bp, x, XN_FLAG_COMPAT, X509_FLAG_COMPAT); +} diff --git a/doc/build.info b/doc/build.info index da0e7a637f7ba..43bb3c8a1df86 100644 --- a/doc/build.info +++ b/doc/build.info @@ -2803,6 +2803,10 @@ DEPEND[html/man3/X509_ACERT_get0_holder_baseCertId.html]=man3/X509_ACERT_get0_ho GENERATE[html/man3/X509_ACERT_get0_holder_baseCertId.html]=man3/X509_ACERT_get0_holder_baseCertId.pod DEPEND[man/man3/X509_ACERT_get0_holder_baseCertId.3]=man3/X509_ACERT_get0_holder_baseCertId.pod GENERATE[man/man3/X509_ACERT_get0_holder_baseCertId.3]=man3/X509_ACERT_get0_holder_baseCertId.pod +DEPEND[html/man3/X509_ACERT_print_ex.html]=man3/X509_ACERT_print_ex.pod +GENERATE[html/man3/X509_ACERT_print_ex.html]=man3/X509_ACERT_print_ex.pod +DEPEND[man/man3/X509_ACERT_print_ex.3]=man3/X509_ACERT_print_ex.pod +GENERATE[man/man3/X509_ACERT_print_ex.3]=man3/X509_ACERT_print_ex.pod DEPEND[html/man3/X509_ALGOR_dup.html]=man3/X509_ALGOR_dup.pod GENERATE[html/man3/X509_ALGOR_dup.html]=man3/X509_ALGOR_dup.pod DEPEND[man/man3/X509_ALGOR_dup.3]=man3/X509_ALGOR_dup.pod @@ -3636,6 +3640,7 @@ html/man3/UI_new.html \ html/man3/X509V3_get_d2i.html \ html/man3/X509V3_set_ctx.html \ html/man3/X509_ACERT_get0_holder_baseCertId.html \ +html/man3/X509_ACERT_print_ex.html \ html/man3/X509_ALGOR_dup.html \ html/man3/X509_ATTRIBUTE.html \ html/man3/X509_CRL_get0_by_serial.html \ @@ -4282,6 +4287,7 @@ man/man3/UI_new.3 \ man/man3/X509V3_get_d2i.3 \ man/man3/X509V3_set_ctx.3 \ man/man3/X509_ACERT_get0_holder_baseCertId.3 \ +man/man3/X509_ACERT_print_ex.3 \ man/man3/X509_ALGOR_dup.3 \ man/man3/X509_ATTRIBUTE.3 \ man/man3/X509_CRL_get0_by_serial.3 \ diff --git a/doc/man3/X509_ACERT_print_ex.pod b/doc/man3/X509_ACERT_print_ex.pod new file mode 100644 index 0000000000000..3a5e0c7a518df --- /dev/null +++ b/doc/man3/X509_ACERT_print_ex.pod @@ -0,0 +1,112 @@ +=pod + +=head1 NAME + +X509_ACERT_print_ex, X509_ACERT_print +- X509_ACERT printing routines + +=head1 SYNOPSIS + + #include + + int X509_ACERT_print(BIO *bp, X509_ACERT *acert); + int X509_ACERT_print_ex(BIO *bp, X509_ACERT *acert, unsigned long nmflags, + unsigned long cflag); + +=head1 DESCRIPTION + +X509_ACERT_print_ex() prints a human readable version of the attribute +certificate I to BIO I. + +The following data contained in the attribute certificate is printed +in order: + +=over 4 + +=item * + +The header text "Attribute certificate:" and "Data:" (X509_FLAG_NO_HEADER) + += item * + +The attribute certificate version number as defined by the standard, +followed in parentheses by the value contained in the version field in +hexadecimal notation. If the version number is not a valid value according +to the specification, only the raw value is printed. +See X509_ACERT_get_version(3) for details. (X509_FLAG_NO_VERSION) + += item * + +The serial number of the attribute certificate (X509_FLAG_NO_SERIAL) + += item * + +The identity of the holder of the attribute certificate. If the +holder issuer name is present, the first GENERAL_NAME +returned by X509_ACERT_get0_holder_entityName() is printed. +If the holder baseCertificateId is present, the issuer name +(printed with X509_NAME_print_ex) and serial number of the +holder's certificate are displayed. (X509_FLAG_NO_SUBJECT) + += item * + +The name of the attribute certificate issuer as returned from +X509_ACERT_get0_issuerName() and printed using X509_NAME_print_ex(). +(X509_FLAG_NO_ISSUER) + += item * + +The period of validity between the times returned from X509_ACERT_get0_notBefore() +and X509_ACERT_get0_notAfter(). The values are printed as a generalized times +using ASN1_GENERALIZEDTIME_print(). (X509_FLAG_NO_VALIDITY) + += item * + +The list of attributes contained in the attribute certificate. +The attribute type is printed with i2a_ASN1_OBJECT(). String valued +attributes are printed as raw string data. ASN1 encoded values are +printed with ASN1_parse_dump(). (X509_FLAG_NO_ATTRIBUTES) + += item * + +All X.509 extensions contained in the attribute certificate. (X509_FLAG_NO_EXTENSIONS) + += item * + +The signature is printed with X509_signature_print(). (X509_FLAG_NO_SIGDUMP) + +If I is specifies as X509_FLAG_COMPAT, all of the above data in the +attribute certificate will be printed. + +The I flag determines the format used to output all fields printed using +X509_NAME_print_ex(). See L for details. + +X509_ACERT_print() is equivalent to calling X509_ACERT_print_ex() with the +I and I set to XN_FLAG_COMPAT and X509_FLAG_COMPAT +respectively. + +=back + +=head1 RETURN VALUES + +X509_ACERT_print_ex() X509_ACERT_print() return 1 for +success and 0 for failure. + +=head1 SEE ALSO + +L + +=head1 HISTORY + +X509_ACERT_print() and X509_ACERT_print_ex() were added in OpenSSL 3.4. + +=head1 COPYRIGHT + +Copyright 2023 The OpenSSL Project Authors. All Rights Reserved. + +Licensed under the Apache License 2.0 (the "License"). You may not use +this file except in compliance with the License. You can obtain a copy +in the file LICENSE in the source distribution or at +L. + +=cut diff --git a/include/openssl/x509_acert.h.in b/include/openssl/x509_acert.h.in index 826f39d423c53..867eab7869bfd 100644 --- a/include/openssl/x509_acert.h.in +++ b/include/openssl/x509_acert.h.in @@ -62,6 +62,10 @@ const ASN1_TIME *X509_ACERT_get0_notBefore(const X509_ACERT *x); const ASN1_TIME *X509_ACERT_get0_notAfter(const X509_ACERT *x); const ASN1_BIT_STRING *X509_ACERT_get0_issuerUID(const X509_ACERT *x); +int X509_ACERT_print(BIO *bp, X509_ACERT *x); +int X509_ACERT_print_ex(BIO *bp, X509_ACERT *x, unsigned long nmflags, + unsigned long cflag); + # define OSSL_OBJECT_DIGEST_INFO_PUBLIC_KEY 0 # define OSSL_OBJECT_DIGEST_INFO_PUBLIC_KEY_CERT 1 # define OSSL_OBJECT_DIGEST_INFO_OTHER 2 /* must not be used in RFC 5755 profile */ diff --git a/util/libcrypto.num b/util/libcrypto.num index 082de0db1f29d..f8ac4204ac371 100644 --- a/util/libcrypto.num +++ b/util/libcrypto.num @@ -5599,3 +5599,5 @@ OSSL_ISSUER_SERIAL_get0_issuerUID ? 3_4_0 EXIST::FUNCTION: OSSL_ISSUER_SERIAL_set1_issuer ? 3_4_0 EXIST::FUNCTION: OSSL_ISSUER_SERIAL_set1_serial ? 3_4_0 EXIST::FUNCTION: OSSL_ISSUER_SERIAL_set1_issuerUID ? 3_4_0 EXIST::FUNCTION: +X509_ACERT_print ? 3_4_0 EXIST::FUNCTION: +X509_ACERT_print_ex ? 3_4_0 EXIST::FUNCTION: From ff377c34c883654daa0005bca1997dd7a9bcb475 Mon Sep 17 00:00:00 2001 From: Damian Hobson-Garcia Date: Fri, 30 Jun 2023 17:12:38 -0400 Subject: [PATCH 04/12] x509_acert: Add, remove and get attribute certificate attributes --- crypto/x509/x509_acert.c | 59 +++++++++++++++++++++++++++ doc/build.info | 12 ++++++ doc/man3/X509_ACERT_add1_attr.pod | 67 +++++++++++++++++++++++++++++++ doc/man3/X509_ACERT_get_attr.pod | 59 +++++++++++++++++++++++++++ include/openssl/x509_acert.h.in | 15 +++++++ util/libcrypto.num | 9 +++++ 6 files changed, 221 insertions(+) create mode 100644 doc/man3/X509_ACERT_add1_attr.pod create mode 100644 doc/man3/X509_ACERT_get_attr.pod diff --git a/crypto/x509/x509_acert.c b/crypto/x509/x509_acert.c index d6b78bfc7cad3..7e0d5f139b4bf 100644 --- a/crypto/x509/x509_acert.c +++ b/crypto/x509/x509_acert.c @@ -183,3 +183,62 @@ const ASN1_GENERALIZEDTIME *X509_ACERT_get0_notAfter(const X509_ACERT *x) { return x->acinfo->validityPeriod.notAfter; } + +/* Attribute management functions */ + +int X509_ACERT_get_attr_count(const X509_ACERT *x) +{ + return X509at_get_attr_count(x->acinfo->attributes); +} + +int X509_ACERT_get_attr_by_NID(const X509_ACERT *x, int nid, int lastpos) +{ + return X509at_get_attr_by_NID(x->acinfo->attributes, nid, lastpos); +} + +int X509_ACERT_get_attr_by_OBJ(const X509_ACERT *x, const ASN1_OBJECT *obj, + int lastpos) +{ + return X509at_get_attr_by_OBJ(x->acinfo->attributes, obj, lastpos); +} + +X509_ATTRIBUTE *X509_ACERT_get_attr(const X509_ACERT *x, int loc) +{ + return X509at_get_attr(x->acinfo->attributes, loc); +} + +X509_ATTRIBUTE *X509_ACERT_delete_attr(X509_ACERT *x, int loc) +{ + return X509at_delete_attr(x->acinfo->attributes, loc); +} + +int X509_ACERT_add1_attr(X509_ACERT *x, X509_ATTRIBUTE *attr) +{ + STACK_OF(X509_ATTRIBUTE) **attrs = &x->acinfo->attributes; + + return X509at_add1_attr(attrs, attr) != NULL; +} + +int X509_ACERT_add1_attr_by_OBJ(X509_ACERT *x, const ASN1_OBJECT *obj, + int type, const void *bytes, int len) +{ + STACK_OF(X509_ATTRIBUTE) **attrs = &x->acinfo->attributes; + + return X509at_add1_attr_by_OBJ(attrs, obj, type, bytes, len) != NULL; +} + +int X509_ACERT_add1_attr_by_NID(X509_ACERT *x, int nid, int type, + const void *bytes, int len) +{ + STACK_OF(X509_ATTRIBUTE) **attrs = &x->acinfo->attributes; + + return X509at_add1_attr_by_NID(attrs, nid, type, bytes, len) != NULL; +} + +int X509_ACERT_add1_attr_by_txt(X509_ACERT *x, const char *attrname, int type, + const unsigned char *bytes, int len) +{ + STACK_OF(X509_ATTRIBUTE) **attrs = &x->acinfo->attributes; + + return X509at_add1_attr_by_txt(attrs, attrname, type, bytes, len) != NULL; +} diff --git a/doc/build.info b/doc/build.info index 43bb3c8a1df86..cc8bdef61d80a 100644 --- a/doc/build.info +++ b/doc/build.info @@ -2799,10 +2799,18 @@ DEPEND[html/man3/X509V3_set_ctx.html]=man3/X509V3_set_ctx.pod GENERATE[html/man3/X509V3_set_ctx.html]=man3/X509V3_set_ctx.pod DEPEND[man/man3/X509V3_set_ctx.3]=man3/X509V3_set_ctx.pod GENERATE[man/man3/X509V3_set_ctx.3]=man3/X509V3_set_ctx.pod +DEPEND[html/man3/X509_ACERT_add1_attr.html]=man3/X509_ACERT_add1_attr.pod +GENERATE[html/man3/X509_ACERT_add1_attr.html]=man3/X509_ACERT_add1_attr.pod +DEPEND[man/man3/X509_ACERT_add1_attr.3]=man3/X509_ACERT_add1_attr.pod +GENERATE[man/man3/X509_ACERT_add1_attr.3]=man3/X509_ACERT_add1_attr.pod DEPEND[html/man3/X509_ACERT_get0_holder_baseCertId.html]=man3/X509_ACERT_get0_holder_baseCertId.pod GENERATE[html/man3/X509_ACERT_get0_holder_baseCertId.html]=man3/X509_ACERT_get0_holder_baseCertId.pod DEPEND[man/man3/X509_ACERT_get0_holder_baseCertId.3]=man3/X509_ACERT_get0_holder_baseCertId.pod GENERATE[man/man3/X509_ACERT_get0_holder_baseCertId.3]=man3/X509_ACERT_get0_holder_baseCertId.pod +DEPEND[html/man3/X509_ACERT_get_attr.html]=man3/X509_ACERT_get_attr.pod +GENERATE[html/man3/X509_ACERT_get_attr.html]=man3/X509_ACERT_get_attr.pod +DEPEND[man/man3/X509_ACERT_get_attr.3]=man3/X509_ACERT_get_attr.pod +GENERATE[man/man3/X509_ACERT_get_attr.3]=man3/X509_ACERT_get_attr.pod DEPEND[html/man3/X509_ACERT_print_ex.html]=man3/X509_ACERT_print_ex.pod GENERATE[html/man3/X509_ACERT_print_ex.html]=man3/X509_ACERT_print_ex.pod DEPEND[man/man3/X509_ACERT_print_ex.3]=man3/X509_ACERT_print_ex.pod @@ -3639,7 +3647,9 @@ html/man3/UI_create_method.html \ html/man3/UI_new.html \ html/man3/X509V3_get_d2i.html \ html/man3/X509V3_set_ctx.html \ +html/man3/X509_ACERT_add1_attr.html \ html/man3/X509_ACERT_get0_holder_baseCertId.html \ +html/man3/X509_ACERT_get_attr.html \ html/man3/X509_ACERT_print_ex.html \ html/man3/X509_ALGOR_dup.html \ html/man3/X509_ATTRIBUTE.html \ @@ -4286,7 +4296,9 @@ man/man3/UI_create_method.3 \ man/man3/UI_new.3 \ man/man3/X509V3_get_d2i.3 \ man/man3/X509V3_set_ctx.3 \ +man/man3/X509_ACERT_add1_attr.3 \ man/man3/X509_ACERT_get0_holder_baseCertId.3 \ +man/man3/X509_ACERT_get_attr.3 \ man/man3/X509_ACERT_print_ex.3 \ man/man3/X509_ALGOR_dup.3 \ man/man3/X509_ATTRIBUTE.3 \ diff --git a/doc/man3/X509_ACERT_add1_attr.pod b/doc/man3/X509_ACERT_add1_attr.pod new file mode 100644 index 0000000000000..ab6a25667048f --- /dev/null +++ b/doc/man3/X509_ACERT_add1_attr.pod @@ -0,0 +1,67 @@ +=pod + +=head1 NAME + +X509_ACERT_add1_attr, +X509_ACERT_add1_attr_by_NID, +X509_ACERT_add1_attr_by_OBJ, +X509_ACERT_add1_attr_by_txt, +X509_ACERT_delete_attr +- X509_ACERT attribute functions + +=head1 SYNOPSIS + + #include + + int X509_ACERT_add1_attr(X509_ACERT *x, X509_ATTRIBUTE *attr); + int X509_ACERT_add1_attr_by_NID(X509_ACERT *x, int nid, int type, + const void *bytes, int len); + int X509_ACERT_add1_attr_by_OBJ(X509_ACERT *x, const ASN1_OBJECT *obj, + int type, const void *bytes, int len); + int X509_ACERT_add1_attr_by_txt(X509_ACERT *x, const char *attrname, int type, + const unsigned char *bytes, int len); + X509_ATTRIBUTE *X509_ACERT_delete_attr(X509_ACERT *x, int loc); + +=head1 DESCRIPTION + +X509_ACERT_add1_attr() adds a constructed X509_ATTRIBUTE B to the +existing X509_ACERT structure B. + +X509_ACERT_add1_attr_by_NID() and X509_ACERT_add1_attr_by_OBJ() +add an attribute of type I or I with a value of ASN1 +type I constructed using I bytes from I. + +X509_ACERT_add1_attr_by_txt() adds an attribute of type I with a value of +ASN1 type I constructed using I bytes from I. + +X509_ACERT_delete_attr() will delete the Ith attribute from I and +return a pointer to it or NULL if there are fewer than I attributes +contained in I. + +=head1 RETURN VALUES + +X509_ACERT_add1_attr(), X509_ACERT_add1_attr_by_NID(), and +X509_ACERT_add1_attr_by_OBJ() return 1 for success and 0 for failure. + +X509_ACERT_delete_attr() returns a B pointer on +success or NULL on failure. + +=head1 SEE ALSO + +L + +=head1 HISTORY + +X509_ACERT_add1_attr(), X509_ACERT_add1_attr_by_NID(), X509_ACERT_add1_attr_by_OBJ(), +X509_ACERT_add1_attr_by_txt() and X509_ACERT_delete_attr() were added in OpenSSL 3.4. + +=head1 COPYRIGHT + +Copyright 2023 The OpenSSL Project Authors. All Rights Reserved. + +Licensed under the Apache License 2.0 (the "License"). You may not use +this file except in compliance with the License. You can obtain a copy +in the file LICENSE in the source distribution or at +L. + +=cut diff --git a/doc/man3/X509_ACERT_get_attr.pod b/doc/man3/X509_ACERT_get_attr.pod new file mode 100644 index 0000000000000..b61cc2510bff7 --- /dev/null +++ b/doc/man3/X509_ACERT_get_attr.pod @@ -0,0 +1,59 @@ +=pod + +=head1 NAME + +X509_ACERT_get_attr, +X509_ACERT_get_attr_by_NID, +X509_ACERT_get_attr_by_OBJ, +X509_ACERT_get_attr_count +- Retrieve attributes from an X509_ACERT structure + +=head1 SYNOPSIS + + #include + + X509_ATTRIBUTE *X509_ACERT_get_attr(const X509_ACERT *x, int loc); + int X509_ACERT_get_attr_by_NID(const X509_ACERT *x, int nid, int lastpos); + int X509_ACERT_get_attr_by_OBJ(const X509_ACERT *x, const ASN1_OBJECT *obj, + int lastpos); + int X509_ACERT_get_attr_count(const X509_ACERT *x); + +=head1 DESCRIPTION + +X509_ACERT_get0_attr() retrieves the Ith B from an +B I. X509_ACERT_get_attr_count() returns the total number +of attributes in the B. + +X509_ACERT_get_attr_by_NID() and X509_ACERT_get_attr_by_OBJ() retrieve the next +attribute location matching I or I after I. I +should initially be set to -1. +If there are no more entries -1 is returned. If I is invalid +(doesn't correspond to a valid OID) then -2 is returned. + +=head1 RETURN VALUES + +X509_ACERT_get0_attr() return a B from an attribute +certificate, or NULL if the specified attribute is not found. + +X509_ACERT_get_attr_by_NID() and X509_ACERT_get_attr_by_OBJ() return +the location of the next attribute requested or -1 if not found. +X509_ACERT_get_attr_by_NID() can also return -2 if the supplied NID is invalid. + +X509_ACERT_get_attr_count() returns the number of attributes in the given +attribute certificate. + +=head1 HISTORY + +X509_ACERT_get0_attr(), X509_ACERT_get_attr_by_NID(), X509_ACERT_get_attr_by_OBJ() and +X509_ACERT_get_attr_count() were added in OpenSSL 3.4. + +=head1 COPYRIGHT + +Copyright 2023 The OpenSSL Project Authors. All Rights Reserved. + +Licensed under the Apache License 2.0 (the "License"). You may not use +this file except in compliance with the License. You can obtain a copy +in the file LICENSE in the source distribution or at +L. + +=cut diff --git a/include/openssl/x509_acert.h.in b/include/openssl/x509_acert.h.in index 867eab7869bfd..8e5744c0d8333 100644 --- a/include/openssl/x509_acert.h.in +++ b/include/openssl/x509_acert.h.in @@ -66,6 +66,13 @@ int X509_ACERT_print(BIO *bp, X509_ACERT *x); int X509_ACERT_print_ex(BIO *bp, X509_ACERT *x, unsigned long nmflags, unsigned long cflag); +int X509_ACERT_get_attr_count(const X509_ACERT *x); +int X509_ACERT_get_attr_by_NID(const X509_ACERT *x, int nid, int lastpos); +int X509_ACERT_get_attr_by_OBJ(const X509_ACERT *x, const ASN1_OBJECT *obj, + int lastpos); +X509_ATTRIBUTE *X509_ACERT_get_attr(const X509_ACERT *x, int loc); +X509_ATTRIBUTE *X509_ACERT_delete_attr(X509_ACERT *x, int loc); + # define OSSL_OBJECT_DIGEST_INFO_PUBLIC_KEY 0 # define OSSL_OBJECT_DIGEST_INFO_PUBLIC_KEY_CERT 1 # define OSSL_OBJECT_DIGEST_INFO_OTHER 2 /* must not be used in RFC 5755 profile */ @@ -75,6 +82,14 @@ void X509_ACERT_set0_holder_baseCertId(X509_ACERT *x, OSSL_ISSUER_SERIAL *isss); void X509_ACERT_set0_holder_digest(X509_ACERT *x, OSSL_OBJECT_DIGEST_INFO *dinfo); +int X509_ACERT_add1_attr(X509_ACERT *x, X509_ATTRIBUTE *attr); +int X509_ACERT_add1_attr_by_OBJ(X509_ACERT *x, const ASN1_OBJECT *obj, + int type, const void *bytes, int len); +int X509_ACERT_add1_attr_by_NID(X509_ACERT *x, int nid, int type, + const void *bytes, int len); +int X509_ACERT_add1_attr_by_txt(X509_ACERT *x, const char *attrname, int type, + const unsigned char *bytes, int len); + int X509_ACERT_set1_issuerName(X509_ACERT *x, const X509_NAME *name); int X509_ACERT_set1_serialNumber(X509_ACERT *x, const ASN1_INTEGER *serial); int X509_ACERT_set1_notBefore(X509_ACERT *x, const ASN1_GENERALIZEDTIME *time); diff --git a/util/libcrypto.num b/util/libcrypto.num index f8ac4204ac371..bfb50bbf8722d 100644 --- a/util/libcrypto.num +++ b/util/libcrypto.num @@ -5601,3 +5601,12 @@ OSSL_ISSUER_SERIAL_set1_serial ? 3_4_0 EXIST::FUNCTION: OSSL_ISSUER_SERIAL_set1_issuerUID ? 3_4_0 EXIST::FUNCTION: X509_ACERT_print ? 3_4_0 EXIST::FUNCTION: X509_ACERT_print_ex ? 3_4_0 EXIST::FUNCTION: +X509_ACERT_get_attr_count ? 3_4_0 EXIST::FUNCTION: +X509_ACERT_get_attr_by_NID ? 3_4_0 EXIST::FUNCTION: +X509_ACERT_get_attr_by_OBJ ? 3_4_0 EXIST::FUNCTION: +X509_ACERT_get_attr ? 3_4_0 EXIST::FUNCTION: +X509_ACERT_delete_attr ? 3_4_0 EXIST::FUNCTION: +X509_ACERT_add1_attr ? 3_4_0 EXIST::FUNCTION: +X509_ACERT_add1_attr_by_OBJ ? 3_4_0 EXIST::FUNCTION: +X509_ACERT_add1_attr_by_NID ? 3_4_0 EXIST::FUNCTION: +X509_ACERT_add1_attr_by_txt ? 3_4_0 EXIST::FUNCTION: From bf627921f732960e0e898ae41f576c112b6a92b1 Mon Sep 17 00:00:00 2001 From: Damian Hobson-Garcia Date: Fri, 30 Jun 2023 17:03:57 -0400 Subject: [PATCH 05/12] x509_acert: Add API to sign and verify attribute certificates --- crypto/x509/x_all.c | 26 ++++++++++++++++++++++++++ doc/man3/X509_sign.pod | 10 ++++++++++ doc/man3/X509_verify.pod | 12 +++++++++--- include/openssl/x509_acert.h.in | 4 ++++ util/libcrypto.num | 3 +++ 5 files changed, 52 insertions(+), 3 deletions(-) diff --git a/crypto/x509/x_all.c b/crypto/x509/x_all.c index 95c91a0f206b4..3083eb1dca9b7 100644 --- a/crypto/x509/x_all.c +++ b/crypto/x509/x_all.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -52,6 +53,16 @@ int X509_REQ_verify(X509_REQ *a, EVP_PKEY *r) return X509_REQ_verify_ex(a, r, NULL, NULL); } +int X509_ACERT_verify(X509_ACERT *a, EVP_PKEY *r) +{ + if (X509_ALGOR_cmp(&a->sig_alg, &a->acinfo->signature) != 0) + return 0; + + return ASN1_item_verify_ex(ASN1_ITEM_rptr(X509_ACERT_INFO), &a->sig_alg, + &a->signature, a->acinfo, + NULL, r, NULL, NULL); +} + int NETSCAPE_SPKI_verify(NETSCAPE_SPKI *a, EVP_PKEY *r) { return ASN1_item_verify(ASN1_ITEM_rptr(NETSCAPE_SPKAC), @@ -174,6 +185,21 @@ X509_CRL *X509_CRL_load_http(const char *url, BIO *bio, BIO *rbio, int timeout) ASN1_ITEM_rptr(X509_CRL)); } +int X509_ACERT_sign(X509_ACERT *x, EVP_PKEY *pkey, const EVP_MD *md) +{ + return ASN1_item_sign_ex(ASN1_ITEM_rptr(X509_ACERT_INFO), &x->sig_alg, + &x->acinfo->signature, + &x->signature, x->acinfo, NULL, + pkey, md, NULL, NULL); +} + +int X509_ACERT_sign_ctx(X509_ACERT *x, EVP_MD_CTX *ctx) +{ + return ASN1_item_sign_ctx(ASN1_ITEM_rptr(X509_ACERT_INFO), + &x->sig_alg, &x->acinfo->signature, &x->signature, + &x->acinfo, ctx); +} + int NETSCAPE_SPKI_sign(NETSCAPE_SPKI *x, EVP_PKEY *pkey, const EVP_MD *md) { return diff --git a/doc/man3/X509_sign.pod b/doc/man3/X509_sign.pod index 7ca8a1a55ecf8..75c968b1146f8 100644 --- a/doc/man3/X509_sign.pod +++ b/doc/man3/X509_sign.pod @@ -4,6 +4,7 @@ X509_sign, X509_sign_ctx, X509_REQ_sign, X509_REQ_sign_ctx, +X509_ACERT_sign, X509_ACERT_sign_ctx, X509_CRL_sign, X509_CRL_sign_ctx - sign certificate, certificate request, or CRL signature @@ -20,6 +21,11 @@ sign certificate, certificate request, or CRL signature int X509_CRL_sign(X509_CRL *x, EVP_PKEY *pkey, const EVP_MD *md); int X509_CRL_sign_ctx(X509_CRL *x, EVP_MD_CTX *ctx); + #include + + int X509_ACERT_sign(X509_ACERT *x, EVP_PKEY *pkey, const EVP_MD *md); + int X509_ACERT_sign_ctx(X509_ACERT *x, EVP_MD_CTX *ctx); + =head1 DESCRIPTION X509_sign() signs certificate I using private key I and message @@ -29,6 +35,7 @@ If the certificate information includes X.509 extensions, these two functions make sure that the certificate bears X.509 version 3. X509_REQ_sign(), X509_REQ_sign_ctx(), +X509_ACERT_sign(), X509_ACERT_sign_ctx(), X509_CRL_sign(), and X509_CRL_sign_ctx() sign certificate requests and CRLs, respectively. @@ -68,6 +75,9 @@ available in all versions of OpenSSL. The X509_sign_ctx(), X509_REQ_sign_ctx() and X509_CRL_sign_ctx() functions were added in OpenSSL 1.0.1. +The X509_ACERT_sign() and X509_ACERT_sign_ctx() functions were added +in OpenSSL 3.4. + =head1 COPYRIGHT Copyright 2015-2023 The OpenSSL Project Authors. All Rights Reserved. diff --git a/doc/man3/X509_verify.pod b/doc/man3/X509_verify.pod index 8cdb2154ae511..e6c35675f80c7 100644 --- a/doc/man3/X509_verify.pod +++ b/doc/man3/X509_verify.pod @@ -4,7 +4,7 @@ X509_verify, X509_self_signed, X509_REQ_verify_ex, X509_REQ_verify, -X509_CRL_verify - +X509_CRL_verify, X509_ACERT_verify - verify certificate, certificate request, or CRL signature =head1 SYNOPSIS @@ -19,6 +19,9 @@ verify certificate, certificate request, or CRL signature int X509_REQ_verify(X509_REQ *a, EVP_PKEY *r); int X509_CRL_verify(X509_CRL *a, EVP_PKEY *r); + #include + int X509_ACERT_verify(X509_CRL *a, EVP_PKEY *r); + =head1 DESCRIPTION X509_verify() verifies the signature of certificate I using public key @@ -31,8 +34,9 @@ authority key identifier (if present) must match the subject key identifier etc. The signature itself is actually verified only if B is 1, as for explicitly trusted certificates this verification is not worth the effort. -X509_REQ_verify_ex(), X509_REQ_verify() and X509_CRL_verify() -verify the signatures of certificate requests and CRLs, respectively. +X509_REQ_verify_ex(), X509_REQ_verify(), X509_CRL_verify() and X509_ACERT_verify() +verify the signatures of certificate requests, CRLs and attribute certificates +respectively. =head1 RETURN VALUES @@ -71,6 +75,8 @@ functions are available in all versions of OpenSSL. X509_REQ_verify_ex(), and X509_self_signed() were added in OpenSSL 3.0. +X509_ACERT_verify() was added in OpenSSL 3.4. + =head1 COPYRIGHT Copyright 2015-2023 The OpenSSL Project Authors. All Rights Reserved. diff --git a/include/openssl/x509_acert.h.in b/include/openssl/x509_acert.h.in index 8e5744c0d8333..cbbb43ec86fdd 100644 --- a/include/openssl/x509_acert.h.in +++ b/include/openssl/x509_acert.h.in @@ -45,6 +45,10 @@ DECLARE_PEM_rw(X509_ACERT, X509_ACERT) X509_ACERT *d2i_X509_ACERT_bio(BIO *bp, X509_ACERT **acert); int i2d_X509_ACERT_bio(BIO *bp, const X509_ACERT *acert); +int X509_ACERT_sign(X509_ACERT *x, EVP_PKEY *pkey, const EVP_MD *md); +int X509_ACERT_sign_ctx(X509_ACERT *x, EVP_MD_CTX *ctx); +int X509_ACERT_verify(X509_ACERT *a, EVP_PKEY *r); + # define X509_ACERT_VERSION_2 1 const GENERAL_NAMES *X509_ACERT_get0_holder_entityName(const X509_ACERT *x); diff --git a/util/libcrypto.num b/util/libcrypto.num index bfb50bbf8722d..514f7d91fc0e6 100644 --- a/util/libcrypto.num +++ b/util/libcrypto.num @@ -5610,3 +5610,6 @@ X509_ACERT_add1_attr ? 3_4_0 EXIST::FUNCTION: X509_ACERT_add1_attr_by_OBJ ? 3_4_0 EXIST::FUNCTION: X509_ACERT_add1_attr_by_NID ? 3_4_0 EXIST::FUNCTION: X509_ACERT_add1_attr_by_txt ? 3_4_0 EXIST::FUNCTION: +X509_ACERT_sign ? 3_4_0 EXIST::FUNCTION: +X509_ACERT_sign_ctx ? 3_4_0 EXIST::FUNCTION: +X509_ACERT_verify ? 3_4_0 EXIST::FUNCTION: From 0af44a456ae89d32f27d8ec8b52aecceb03ff826 Mon Sep 17 00:00:00 2001 From: Damian Hobson-Garcia Date: Fri, 30 Jun 2023 17:12:57 -0400 Subject: [PATCH 06/12] x509_acert: Add and retrieve certificate extensions Add API to manage attribute certificate extensions --- crypto/x509/x509_acert.c | 16 ++++++++++++++++ doc/man3/X509V3_get_d2i.pod | 19 +++++++++++++++---- include/openssl/x509_acert.h.in | 5 +++++ util/libcrypto.num | 3 +++ 4 files changed, 39 insertions(+), 4 deletions(-) diff --git a/crypto/x509/x509_acert.c b/crypto/x509/x509_acert.c index 7e0d5f139b4bf..9499a14cc0bb1 100644 --- a/crypto/x509/x509_acert.c +++ b/crypto/x509/x509_acert.c @@ -242,3 +242,19 @@ int X509_ACERT_add1_attr_by_txt(X509_ACERT *x, const char *attrname, int type, return X509at_add1_attr_by_txt(attrs, attrname, type, bytes, len) != NULL; } + +void *X509_ACERT_get_ext_d2i(const X509_ACERT *x, int nid, int *crit, int *idx) +{ + return X509V3_get_d2i(x->acinfo->extensions, nid, crit, idx); +} + +int X509_ACERT_add1_ext_i2d(X509_ACERT *x, int nid, void *value, int crit, + unsigned long flags) +{ + return X509V3_add1_i2d(&x->acinfo->extensions, nid, value, crit, flags); +} + +const STACK_OF(X509_EXTENSION) *X509_ACERT_get0_extensions(const X509_ACERT *x) +{ + return x->acinfo->extensions; +} diff --git a/doc/man3/X509V3_get_d2i.pod b/doc/man3/X509V3_get_d2i.pod index 4a2e81b0dbdff..b5abd2ff28c85 100644 --- a/doc/man3/X509V3_get_d2i.pod +++ b/doc/man3/X509V3_get_d2i.pod @@ -4,9 +4,10 @@ X509V3_get_d2i, X509V3_add1_i2d, X509V3_EXT_d2i, X509V3_EXT_i2d, X509_get_ext_d2i, X509_add1_ext_i2d, +X509_ACERT_get_ext_d2i, X509_ACERT_add1_ext_i2d, X509_CRL_get_ext_d2i, X509_CRL_add1_ext_i2d, X509_REVOKED_get_ext_d2i, X509_REVOKED_add1_ext_i2d, -X509_get0_extensions, X509_CRL_get0_extensions, +X509_get0_extensions, X509_ACERT_get0_extensions, X509_CRL_get0_extensions, X509_REVOKED_get0_extensions - X509 extension decode and encode functions =head1 SYNOPSIS @@ -25,6 +26,10 @@ X509_REVOKED_get0_extensions - X509 extension decode and encode functions int X509_add1_ext_i2d(X509 *x, int nid, void *value, int crit, unsigned long flags); + void *X509_ACERT_get_ext_d2i(const X509_ACERT *x, int nid, int *crit, int *idx); + int X509_ACERT_add1_ext_i2d(X509_ACERT *x, int nid, void *value, int crit, + unsigned long flags); + void *X509_CRL_get_ext_d2i(const X509_CRL *crl, int nid, int *crit, int *idx); int X509_CRL_add1_ext_i2d(X509_CRL *crl, int nid, void *value, int crit, unsigned long flags); @@ -34,6 +39,7 @@ X509_REVOKED_get0_extensions - X509 extension decode and encode functions unsigned long flags); const STACK_OF(X509_EXTENSION) *X509_get0_extensions(const X509 *x); + const STACK_OF(X509_EXTENSION) *X509_ACERT_get0_extensions(const X509 *x); const STACK_OF(X509_EXTENSION) *X509_CRL_get0_extensions(const X509_CRL *crl); const STACK_OF(X509_EXTENSION) *X509_REVOKED_get0_extensions(const X509_REVOKED *r); @@ -64,6 +70,10 @@ X509_get_ext_d2i() and X509_add1_ext_i2d() operate on the extensions of certificate I. They are otherwise identical to X509V3_get_d2i() and X509V3_add1_i2d(). +X509_ACERT_get_ext_d2i() and X509_ACERT_add1_ext_i2d() operate on the extensions +of B structure I. They are otherwise identical to X509V3_get_d2i() +and X509V3_add1_i2d(). + X509_CRL_get_ext_d2i() and X509_CRL_add1_ext_i2d() operate on the extensions of CRL I. They are otherwise identical to X509V3_get_d2i() and X509V3_add1_i2d(). @@ -72,9 +82,10 @@ X509_REVOKED_get_ext_d2i() and X509_REVOKED_add1_ext_i2d() operate on the extensions of B structure I (i.e for CRL entry extensions). They are otherwise identical to X509V3_get_d2i() and X509V3_add1_i2d(). -X509_get0_extensions(), X509_CRL_get0_extensions() and -X509_REVOKED_get0_extensions() return a STACK of all the extensions -of a certificate, a CRL or a CRL entry respectively. +X509_get0_extensions(), X509_ACERT_get0_extensions(), +X509_CRL_get0_extensions() and X509_REVOKED_get0_extensions() return a +STACK of all the extensions of a certificate, an attribute certificate, +a CRL or a CRL entry respectively. =head1 NOTES diff --git a/include/openssl/x509_acert.h.in b/include/openssl/x509_acert.h.in index cbbb43ec86fdd..6a56169325f15 100644 --- a/include/openssl/x509_acert.h.in +++ b/include/openssl/x509_acert.h.in @@ -77,6 +77,11 @@ int X509_ACERT_get_attr_by_OBJ(const X509_ACERT *x, const ASN1_OBJECT *obj, X509_ATTRIBUTE *X509_ACERT_get_attr(const X509_ACERT *x, int loc); X509_ATTRIBUTE *X509_ACERT_delete_attr(X509_ACERT *x, int loc); +void *X509_ACERT_get_ext_d2i(const X509_ACERT *x, int nid, int *crit, int *idx); +int X509_ACERT_add1_ext_i2d(X509_ACERT *x, int nid, void *value, int crit, + unsigned long flags); +const STACK_OF(X509_EXTENSION) *X509_ACERT_get0_extensions(const X509_ACERT *x); + # define OSSL_OBJECT_DIGEST_INFO_PUBLIC_KEY 0 # define OSSL_OBJECT_DIGEST_INFO_PUBLIC_KEY_CERT 1 # define OSSL_OBJECT_DIGEST_INFO_OTHER 2 /* must not be used in RFC 5755 profile */ diff --git a/util/libcrypto.num b/util/libcrypto.num index 514f7d91fc0e6..41f0ad0031406 100644 --- a/util/libcrypto.num +++ b/util/libcrypto.num @@ -5613,3 +5613,6 @@ X509_ACERT_add1_attr_by_txt ? 3_4_0 EXIST::FUNCTION: X509_ACERT_sign ? 3_4_0 EXIST::FUNCTION: X509_ACERT_sign_ctx ? 3_4_0 EXIST::FUNCTION: X509_ACERT_verify ? 3_4_0 EXIST::FUNCTION: +X509_ACERT_get_ext_d2i ? 3_4_0 EXIST::FUNCTION: +X509_ACERT_add1_ext_i2d ? 3_4_0 EXIST::FUNCTION: +X509_ACERT_get0_extensions ? 3_4_0 EXIST::FUNCTION: From 4eb47d7ae769eb9585bf254085bef4e5758d67e1 Mon Sep 17 00:00:00 2001 From: Damian Hobson-Garcia Date: Tue, 8 Jun 2021 17:31:57 +0900 Subject: [PATCH 07/12] Add IETFAttrSyntax type support The IETFAtrrSyntax type is used for the values of several attributes defined in RFC 5755 for use with attribute certificates. Specifically this type is used with the "Charging Identity" and "Group" attributes. --- crypto/x509/build.info | 2 +- crypto/x509/x_ietfatt.c | 240 +++++++++++++++++++++++ doc/build.info | 12 ++ doc/man3/OSSL_IETF_ATTR_SYNTAX.pod | 97 +++++++++ doc/man3/OSSL_IETF_ATTR_SYNTAX_print.pod | 41 ++++ doc/man3/X509_dup.pod | 6 + doc/man3/d2i_X509.pod | 2 + include/openssl/x509_acert.h.in | 27 +++ util/libcrypto.num | 14 ++ util/other.syms | 1 + 10 files changed, 441 insertions(+), 1 deletion(-) create mode 100644 crypto/x509/x_ietfatt.c create mode 100644 doc/man3/OSSL_IETF_ATTR_SYNTAX.pod create mode 100644 doc/man3/OSSL_IETF_ATTR_SYNTAX_print.pod diff --git a/crypto/x509/build.info b/crypto/x509/build.info index 873a9838b5dd6..6cebadea77296 100644 --- a/crypto/x509/build.info +++ b/crypto/x509/build.info @@ -16,7 +16,7 @@ SOURCE[../../libcrypto]=\ pcy_cache.c pcy_node.c pcy_data.c pcy_map.c pcy_tree.c pcy_lib.c \ v3_asid.c v3_addr.c v3_tlsf.c v3_admis.c v3_no_rev_avail.c \ v3_soa_id.c v3_no_ass.c v3_group_ac.c v3_single_use.c v3_ind_iss.c \ - x509_acert.c x509aset.c t_acert.c + x509_acert.c x509aset.c t_acert.c x_ietfatt.c IF[{- !$disabled{'deprecated-3.0'} -}] SOURCE[../../libcrypto]=x509type.c diff --git a/crypto/x509/x_ietfatt.c b/crypto/x509/x_ietfatt.c new file mode 100644 index 0000000000000..08db0bafc7200 --- /dev/null +++ b/crypto/x509/x_ietfatt.c @@ -0,0 +1,240 @@ +/* + * Copyright 2021 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include +#include +#include + +/*- + * Definition of IetfAttrSyntax from RFC 5755 4.4 + * + * IetfAttrSyntax ::= SEQUENCE { + * policyAuthority [0] GeneralNames OPTIONAL, + * values SEQUENCE OF CHOICE { + * octets OCTET STRING, + * oid OBJECT IDENTIFIER, + * string UTF8String + * } + * } + * + * Section 4.4.2 states that all values in the sequence MUST use the + * same choice of value (octet, oid or string). + */ + +struct OSSL_IETF_ATTR_SYNTAX_VALUE_st { + int type; + union { + ASN1_OCTET_STRING *octets; + ASN1_OBJECT *oid; + ASN1_UTF8STRING *string; + } u; +}; + +struct OSSL_IETF_ATTR_SYNTAX_st { + GENERAL_NAMES *policyAuthority; + int type; + STACK_OF(OSSL_IETF_ATTR_SYNTAX_VALUE) *values; +}; + +ASN1_CHOICE(OSSL_IETF_ATTR_SYNTAX_VALUE) = { + ASN1_SIMPLE(OSSL_IETF_ATTR_SYNTAX_VALUE, u.octets, ASN1_OCTET_STRING), + ASN1_SIMPLE(OSSL_IETF_ATTR_SYNTAX_VALUE, u.oid, ASN1_OBJECT), + ASN1_SIMPLE(OSSL_IETF_ATTR_SYNTAX_VALUE, u.string, ASN1_UTF8STRING), +} ASN1_CHOICE_END(OSSL_IETF_ATTR_SYNTAX_VALUE) + +ASN1_SEQUENCE(OSSL_IETF_ATTR_SYNTAX) = { + ASN1_IMP_SEQUENCE_OF_OPT(OSSL_IETF_ATTR_SYNTAX, policyAuthority, GENERAL_NAME, 0), + ASN1_SEQUENCE_OF(OSSL_IETF_ATTR_SYNTAX, values, OSSL_IETF_ATTR_SYNTAX_VALUE), +} ASN1_SEQUENCE_END(OSSL_IETF_ATTR_SYNTAX) + +IMPLEMENT_ASN1_ALLOC_FUNCTIONS(OSSL_IETF_ATTR_SYNTAX) +IMPLEMENT_ASN1_ALLOC_FUNCTIONS(OSSL_IETF_ATTR_SYNTAX_VALUE) + +OSSL_IETF_ATTR_SYNTAX *d2i_OSSL_IETF_ATTR_SYNTAX (OSSL_IETF_ATTR_SYNTAX **a, + const unsigned char **in, + long len) +{ + OSSL_IETF_ATTR_SYNTAX *ias; + int i; + + ias = (OSSL_IETF_ATTR_SYNTAX *) ASN1_item_d2i((ASN1_VALUE **)a, in, len, + OSSL_IETF_ATTR_SYNTAX_it()); + if (ias == NULL) + return ias; + + for (i = 0; i < sk_OSSL_IETF_ATTR_SYNTAX_VALUE_num(ias->values); i++) + { + OSSL_IETF_ATTR_SYNTAX_VALUE *val; + + val = sk_OSSL_IETF_ATTR_SYNTAX_VALUE_value(ias->values, i); + if (i == 0) + ias->type = val->type; + else if (val->type != ias->type) + goto invalid_types; + } + + return ias; + +invalid_types: + OSSL_IETF_ATTR_SYNTAX_free(ias); + if (a) + *a = NULL; + ERR_raise(ERR_LIB_X509V3, ERR_R_PASSED_INVALID_ARGUMENT); + return NULL; +} + +int i2d_OSSL_IETF_ATTR_SYNTAX (const OSSL_IETF_ATTR_SYNTAX *a, + unsigned char **out) +{ + return ASN1_item_i2d((const ASN1_VALUE *)a, out, OSSL_IETF_ATTR_SYNTAX_it()); +} + +int OSSL_IETF_ATTR_SYNTAX_get_value_num(const OSSL_IETF_ATTR_SYNTAX *a) +{ + if (a->values == NULL) + return 0; + + return sk_OSSL_IETF_ATTR_SYNTAX_VALUE_num(a->values); +} + +const GENERAL_NAMES * +OSSL_IETF_ATTR_SYNTAX_get0_policyAuthority(const OSSL_IETF_ATTR_SYNTAX *a) +{ + return a->policyAuthority; +} + +void OSSL_IETF_ATTR_SYNTAX_set0_policyAuthority(OSSL_IETF_ATTR_SYNTAX *a, + GENERAL_NAMES *names) +{ + GENERAL_NAMES_free(a->policyAuthority); + a->policyAuthority = names; +} + +void *OSSL_IETF_ATTR_SYNTAX_get0_value(const OSSL_IETF_ATTR_SYNTAX *a, + int ind, int *type) +{ + OSSL_IETF_ATTR_SYNTAX_VALUE *val; + + val = sk_OSSL_IETF_ATTR_SYNTAX_VALUE_value(a->values, ind); + if (val == NULL) + return NULL; + + if (type != NULL) + *type = val->type; + + switch (val->type) { + case OSSL_IETFAS_OCTETS: + return val->u.octets; + case OSSL_IETFAS_OID: + return val->u.oid; + case OSSL_IETFAS_STRING: + return val->u.string; + } + + return NULL; +} + +int OSSL_IETF_ATTR_SYNTAX_add1_value(OSSL_IETF_ATTR_SYNTAX *a, int type, + void *data) +{ + OSSL_IETF_ATTR_SYNTAX_VALUE *val; + + if (data == NULL) + return 0; + + if (a->values == NULL) { + if ((a->values = sk_OSSL_IETF_ATTR_SYNTAX_VALUE_new_null()) == NULL) + goto err; + a->type = type; + } + + if (type != a->type) { + ERR_raise(ERR_LIB_X509V3, ERR_R_PASSED_INVALID_ARGUMENT); + return 0; + } + + if ((val = OSSL_IETF_ATTR_SYNTAX_VALUE_new()) == NULL) + goto err; + + val->type = type; + switch (type) { + case OSSL_IETFAS_OCTETS: + val->u.octets = data; + break; + case OSSL_IETFAS_OID: + val->u.oid = data; + break; + case OSSL_IETFAS_STRING: + val->u.string = data; + break; + default: + ERR_raise(ERR_LIB_X509V3, ERR_R_PASSED_INVALID_ARGUMENT); + return 0; + } + + if (sk_OSSL_IETF_ATTR_SYNTAX_VALUE_push(a->values, val) <= 0) { + OSSL_IETF_ATTR_SYNTAX_VALUE_free(val); + return 0; + } + + return 1; + +err: + ERR_raise(ERR_LIB_X509V3, ERR_R_CRYPTO_LIB); + return 0; +} + +int OSSL_IETF_ATTR_SYNTAX_print(BIO *bp, OSSL_IETF_ATTR_SYNTAX *a, int indent) +{ + int i; + + if (a->policyAuthority != NULL) { + for (i = 0; i < sk_GENERAL_NAME_num(a->policyAuthority); i++) { + if (BIO_printf(bp, "%*s", indent, "") <= 0) + goto err; + + if (GENERAL_NAME_print(bp, sk_GENERAL_NAME_value(a->policyAuthority, + i)) <= 0) + goto err; + + if (BIO_printf(bp, "\n") <= 0) + goto err; + } + } + + for (i = 0; i < OSSL_IETF_ATTR_SYNTAX_get_value_num(a); i++) { + char oidstr[80]; + int ietf_type; + void *attr_value = OSSL_IETF_ATTR_SYNTAX_get0_value(a, i, &ietf_type); + + if (attr_value == NULL) + goto err; + + if (BIO_printf(bp, "%*s", indent, "") <= 0) + goto err; + + switch (ietf_type) { + case OSSL_IETFAS_OID: + OBJ_obj2txt(oidstr, sizeof(oidstr), attr_value, 0); + BIO_printf(bp, "%.*s", (int) sizeof(oidstr), oidstr); + break; + case OSSL_IETFAS_OCTETS: + case OSSL_IETFAS_STRING: + ASN1_STRING_print(bp, attr_value); + break; + } + } + if (BIO_printf(bp, "\n") <= 0) + goto err; + + return 1; + +err: + return 0; +} diff --git a/doc/build.info b/doc/build.info index cc8bdef61d80a..373f61476e348 100644 --- a/doc/build.info +++ b/doc/build.info @@ -1719,6 +1719,14 @@ DEPEND[html/man3/OSSL_HTTP_transfer.html]=man3/OSSL_HTTP_transfer.pod GENERATE[html/man3/OSSL_HTTP_transfer.html]=man3/OSSL_HTTP_transfer.pod DEPEND[man/man3/OSSL_HTTP_transfer.3]=man3/OSSL_HTTP_transfer.pod GENERATE[man/man3/OSSL_HTTP_transfer.3]=man3/OSSL_HTTP_transfer.pod +DEPEND[html/man3/OSSL_IETF_ATTR_SYNTAX.html]=man3/OSSL_IETF_ATTR_SYNTAX.pod +GENERATE[html/man3/OSSL_IETF_ATTR_SYNTAX.html]=man3/OSSL_IETF_ATTR_SYNTAX.pod +DEPEND[man/man3/OSSL_IETF_ATTR_SYNTAX.3]=man3/OSSL_IETF_ATTR_SYNTAX.pod +GENERATE[man/man3/OSSL_IETF_ATTR_SYNTAX.3]=man3/OSSL_IETF_ATTR_SYNTAX.pod +DEPEND[html/man3/OSSL_IETF_ATTR_SYNTAX_print.html]=man3/OSSL_IETF_ATTR_SYNTAX_print.pod +GENERATE[html/man3/OSSL_IETF_ATTR_SYNTAX_print.html]=man3/OSSL_IETF_ATTR_SYNTAX_print.pod +DEPEND[man/man3/OSSL_IETF_ATTR_SYNTAX_print.3]=man3/OSSL_IETF_ATTR_SYNTAX_print.pod +GENERATE[man/man3/OSSL_IETF_ATTR_SYNTAX_print.3]=man3/OSSL_IETF_ATTR_SYNTAX_print.pod DEPEND[html/man3/OSSL_ITEM.html]=man3/OSSL_ITEM.pod GENERATE[html/man3/OSSL_ITEM.html]=man3/OSSL_ITEM.pod DEPEND[man/man3/OSSL_ITEM.3]=man3/OSSL_ITEM.pod @@ -3377,6 +3385,8 @@ html/man3/OSSL_HPKE_CTX_new.html \ html/man3/OSSL_HTTP_REQ_CTX.html \ html/man3/OSSL_HTTP_parse_url.html \ html/man3/OSSL_HTTP_transfer.html \ +html/man3/OSSL_IETF_ATTR_SYNTAX.html \ +html/man3/OSSL_IETF_ATTR_SYNTAX_print.html \ html/man3/OSSL_ITEM.html \ html/man3/OSSL_LIB_CTX.html \ html/man3/OSSL_PARAM.html \ @@ -4026,6 +4036,8 @@ man/man3/OSSL_HPKE_CTX_new.3 \ man/man3/OSSL_HTTP_REQ_CTX.3 \ man/man3/OSSL_HTTP_parse_url.3 \ man/man3/OSSL_HTTP_transfer.3 \ +man/man3/OSSL_IETF_ATTR_SYNTAX.3 \ +man/man3/OSSL_IETF_ATTR_SYNTAX_print.3 \ man/man3/OSSL_ITEM.3 \ man/man3/OSSL_LIB_CTX.3 \ man/man3/OSSL_PARAM.3 \ diff --git a/doc/man3/OSSL_IETF_ATTR_SYNTAX.pod b/doc/man3/OSSL_IETF_ATTR_SYNTAX.pod new file mode 100644 index 0000000000000..816ad799b0cd0 --- /dev/null +++ b/doc/man3/OSSL_IETF_ATTR_SYNTAX.pod @@ -0,0 +1,97 @@ +=pod + +=head1 NAME + +OSSL_IETF_ATTR_SYNTAX, +OSSL_IETF_ATTR_SYNTAX_get0_policyAuthority, +OSSL_IETF_ATTR_SYNTAX_set0_policyAuthority, +OSSL_IETF_ATTR_SYNTAX_get_value_num, +OSSL_IETF_ATTR_SYNTAX_get0_value, +OSSL_IETF_ATTR_SYNTAX_add1_value +- Accessors and setters for OSSL_IETF_ATTR_SYNTAX + +=head1 SYNOPSIS + + #include + + typedef struct OSSL_IETF_ATTR_SYNTAX_st OSSL_IETF_ATTR_SYNTAX; + + const GENERAL_NAMES * + OSSL_IETF_ATTR_SYNTAX_get0_policyAuthority(const OSSL_IETF_ATTR_SYNTAX *a); + void OSSL_IETF_ATTR_SYNTAX_set0_policyAuthority(OSSL_IETF_ATTR_SYNTAX *a, + GENERAL_NAMES *names); + + int OSSL_IETF_ATTR_SYNTAX_get_value_num(const OSSL_IETF_ATTR_SYNTAX *a); + void *OSSL_IETF_ATTR_SYNTAX_get0_value(const OSSL_IETF_ATTR_SYNTAX *a, + int ind, int *type); + int OSSL_IETF_ATTR_SYNTAX_add1_value(OSSL_IETF_ATTR_SYNTAX *a, int type, + void *data); + +=head1 DESCRIPTION + +B is an opaque structure that represents the +IetfAttrSyntax type defined in RFC 5755 (Section 4.4) for use +as an AttributeValue. + +OSSL_IETF_ATTR_SYNTAX_get0_policyAuthority() and OSSL_IETF_ATTR_SYNTAX_set0_policyAuthority() +get and set the policyAuthority field of the structure. Both routines act on +internal pointers of the structure and must not be freed by the application. + +An B object also holds a sequence of values. +OSSL_IETF_ATTR_SYNTAX_get_value_num() returns the number of values in the +sequence. OSSL_IETF_ATTR_SYNTAX_add1_value(), adds a copy of I of a specified +I to the sequence. The caller should free the I after use. + +OSSL_IETF_ATTR_SYNTAX_get0_value() will return the value and a specific index I +in the sequence or NULL on error. If I is not NULL, the type of the +value will be written to this location. + +The I of the values stored in the B value sequence is +one of the following: + +=over 4 + +=item OSSL_IETFAS_OCTETS + +A pointer to an ASN1_OCTET_STRING + +=item OSSL_IETFAS_OID + +A pointer to an ASN1_OBJECT + +=item OSSL_IETFAS_STRING + +A pointer to an ASN1_UTF8STRING + +=back + +=head1 RETURN VALUES + +OSSL_IETF_ATTR_SYNTAX_get0_policyAuthority() returns an pointer to a +B structure or B if the policy authority has not been +set. + +OSSL_IETF_ATTR_SYNTAX_get_value_num() returns the number of entries in the value +sequence or -1 on error. + +OSSL_IETF_ATTR_SYNTAX_get0_value() returns a pointer to the value at the given index +or NULL if the index is out of range. + +OSSL_IETF_ATTR_SYNTAX_add1_value() returns 1 on success and 0 on failure. + +=head1 HISTORY + +OSSL_IETF_ATTR_SYNTAX_get0_policyAuthority(), OSSL_IETF_ATTR_SYNTAX_set0_policyAuthority(), +OSSL_IETF_ATTR_SYNTAX_get_value_num(), OSSL_IETF_ATTR_SYNTAX_get0_value(), and +OSSL_IETF_ATTR_SYNTAX_add1_value() were added in OpenSSL 3.4. + +=head1 COPYRIGHT + +Copyright 2021 The OpenSSL Project Authors. All Rights Reserved. + +Licensed under the Apache License 2.0 (the "License"). You may not use +this file except in compliance with the License. You can obtain a copy +in the file LICENSE in the source distribution or at +L. + +=cut diff --git a/doc/man3/OSSL_IETF_ATTR_SYNTAX_print.pod b/doc/man3/OSSL_IETF_ATTR_SYNTAX_print.pod new file mode 100644 index 0000000000000..6fa0ddae5e015 --- /dev/null +++ b/doc/man3/OSSL_IETF_ATTR_SYNTAX_print.pod @@ -0,0 +1,41 @@ +=pod + +=head1 NAME + +OSSL_IETF_ATTR_SYNTAX_print - OSSL_IETF_ATTR_SYNTAX printing + +=head1 SYNOPSIS + + #include + + int OSSL_IETF_ATTR_SYNTAX_print(BIO *bp, OSSL_IETF_ATTR_SYNTAX *a, + int indent); + +=head1 DESCRIPTION + +OSSL_IETF_ATTR_SYNTAX_print() prints a human readable version of I to +BIO I. +Each line of the output is indented by I spaces. + +=head1 RETURN VALUES + +OSSL_IETF_ATTR_SYNTAX_print() return 1 on success or 0 on failure. + +=head1 SEE ALSO + +L + +=head1 HISTORY + +OSSL_IETF_ATTR_SYNTAX_print() was added in OpenSSL 3.4. + +=head1 COPYRIGHT + +Copyright 2021-2022 The OpenSSL Project Authors. All Rights Reserved. + +Licensed under the Apache License 2.0 (the "License"). You may not use +this file except in compliance with the License. You can obtain a copy +in the file LICENSE in the source distribution or at +L. + +=cut diff --git a/doc/man3/X509_dup.pod b/doc/man3/X509_dup.pod index 621427e3fbf85..4ecd5c0830c9f 100644 --- a/doc/man3/X509_dup.pod +++ b/doc/man3/X509_dup.pod @@ -79,6 +79,9 @@ GENERAL_NAME_free, GENERAL_NAME_new, GENERAL_SUBTREE_free, GENERAL_SUBTREE_new, +OSSL_IETF_ATTR_SYNTAX_free, +OSSL_IETF_ATTR_SYNTAX_it, +OSSL_IETF_ATTR_SYNTAX_new, IPAddressChoice_free, IPAddressChoice_new, IPAddressFamily_free, @@ -171,6 +174,9 @@ OSSL_CRMF_PKIPUBLICATIONINFO_new, OSSL_CRMF_SINGLEPUBINFO_free, OSSL_CRMF_SINGLEPUBINFO_it, OSSL_CRMF_SINGLEPUBINFO_new, +OSSL_IETF_ATTR_SYNTAX_VALUE_free, +OSSL_IETF_ATTR_SYNTAX_VALUE_it, +OSSL_IETF_ATTR_SYNTAX_VALUE_new, OSSL_ISSUER_SERIAL_free, OSSL_ISSUER_SERIAL_new, OSSL_OBJECT_DIGEST_INFO_free, diff --git a/doc/man3/d2i_X509.pod b/doc/man3/d2i_X509.pod index 6c4464deb46a8..06f764ef8bb8c 100644 --- a/doc/man3/d2i_X509.pod +++ b/doc/man3/d2i_X509.pod @@ -100,6 +100,7 @@ d2i_OSSL_CRMF_MSGS, d2i_OSSL_CRMF_PBMPARAMETER, d2i_OSSL_CRMF_PKIPUBLICATIONINFO, d2i_OSSL_CRMF_SINGLEPUBINFO, +d2i_OSSL_IETF_ATTR_SYNTAX, d2i_OTHERNAME, d2i_PBE2PARAM, d2i_PBEPARAM, @@ -274,6 +275,7 @@ i2d_OSSL_CRMF_MSGS, i2d_OSSL_CRMF_PBMPARAMETER, i2d_OSSL_CRMF_PKIPUBLICATIONINFO, i2d_OSSL_CRMF_SINGLEPUBINFO, +i2d_OSSL_IETF_ATTR_SYNTAX, i2d_OTHERNAME, i2d_PBE2PARAM, i2d_PBEPARAM, diff --git a/include/openssl/x509_acert.h.in b/include/openssl/x509_acert.h.in index 6a56169325f15..a1e6704c0d92f 100644 --- a/include/openssl/x509_acert.h.in +++ b/include/openssl/x509_acert.h.in @@ -124,4 +124,31 @@ int OSSL_ISSUER_SERIAL_set1_serial(OSSL_ISSUER_SERIAL *isss, const ASN1_INTEGER *serial); int OSSL_ISSUER_SERIAL_set1_issuerUID(OSSL_ISSUER_SERIAL *isss, const ASN1_BIT_STRING *uid); + +# define OSSL_IETFAS_OCTETS 0 +# define OSSL_IETFAS_OID 1 +# define OSSL_IETFAS_STRING 2 + +typedef struct OSSL_IETF_ATTR_SYNTAX_VALUE_st OSSL_IETF_ATTR_SYNTAX_VALUE; +typedef struct OSSL_IETF_ATTR_SYNTAX_st OSSL_IETF_ATTR_SYNTAX; +{- + generate_stack_macros("OSSL_IETF_ATTR_SYNTAX_VALUE"); +-} + +DECLARE_ASN1_ITEM(OSSL_IETF_ATTR_SYNTAX_VALUE) +DECLARE_ASN1_ALLOC_FUNCTIONS(OSSL_IETF_ATTR_SYNTAX_VALUE) +DECLARE_ASN1_FUNCTIONS(OSSL_IETF_ATTR_SYNTAX) + +const GENERAL_NAMES * +OSSL_IETF_ATTR_SYNTAX_get0_policyAuthority(const OSSL_IETF_ATTR_SYNTAX *a); +void OSSL_IETF_ATTR_SYNTAX_set0_policyAuthority(OSSL_IETF_ATTR_SYNTAX *a, + GENERAL_NAMES *names); + +int OSSL_IETF_ATTR_SYNTAX_get_value_num(const OSSL_IETF_ATTR_SYNTAX *a); +void *OSSL_IETF_ATTR_SYNTAX_get0_value(const OSSL_IETF_ATTR_SYNTAX *a, + int ind, int *type); +int OSSL_IETF_ATTR_SYNTAX_add1_value(OSSL_IETF_ATTR_SYNTAX *a, int type, + void *data); +int OSSL_IETF_ATTR_SYNTAX_print(BIO *bp, OSSL_IETF_ATTR_SYNTAX *a, int indent); + #endif diff --git a/util/libcrypto.num b/util/libcrypto.num index 41f0ad0031406..ae11b7b02385c 100644 --- a/util/libcrypto.num +++ b/util/libcrypto.num @@ -5616,3 +5616,17 @@ X509_ACERT_verify ? 3_4_0 EXIST::FUNCTION: X509_ACERT_get_ext_d2i ? 3_4_0 EXIST::FUNCTION: X509_ACERT_add1_ext_i2d ? 3_4_0 EXIST::FUNCTION: X509_ACERT_get0_extensions ? 3_4_0 EXIST::FUNCTION: +OSSL_IETF_ATTR_SYNTAX_VALUE_it ? 3_4_0 EXIST::FUNCTION: +OSSL_IETF_ATTR_SYNTAX_VALUE_free ? 3_4_0 EXIST::FUNCTION: +OSSL_IETF_ATTR_SYNTAX_VALUE_new ? 3_4_0 EXIST::FUNCTION: +d2i_OSSL_IETF_ATTR_SYNTAX ? 3_4_0 EXIST::FUNCTION: +i2d_OSSL_IETF_ATTR_SYNTAX ? 3_4_0 EXIST::FUNCTION: +OSSL_IETF_ATTR_SYNTAX_free ? 3_4_0 EXIST::FUNCTION: +OSSL_IETF_ATTR_SYNTAX_new ? 3_4_0 EXIST::FUNCTION: +OSSL_IETF_ATTR_SYNTAX_it ? 3_4_0 EXIST::FUNCTION: +OSSL_IETF_ATTR_SYNTAX_get0_policyAuthority ? 3_4_0 EXIST::FUNCTION: +OSSL_IETF_ATTR_SYNTAX_set0_policyAuthority ? 3_4_0 EXIST::FUNCTION: +OSSL_IETF_ATTR_SYNTAX_get_value_num ? 3_4_0 EXIST::FUNCTION: +OSSL_IETF_ATTR_SYNTAX_get0_value ? 3_4_0 EXIST::FUNCTION: +OSSL_IETF_ATTR_SYNTAX_add1_value ? 3_4_0 EXIST::FUNCTION: +OSSL_IETF_ATTR_SYNTAX_print ? 3_4_0 EXIST::FUNCTION: diff --git a/util/other.syms b/util/other.syms index 84e6bb6ba3efe..f3fb800d06579 100644 --- a/util/other.syms +++ b/util/other.syms @@ -70,6 +70,7 @@ OSSL_ENCODER_CLEANUP datatype OSSL_ENCODER_INSTANCE datatype OSSL_HTTP_bio_cb_t datatype OSSL_HTTP_REQ_CTX datatype +OSSL_IETF_ATTR_SYNTAX datatype OSSL_ITEM datatype OSSL_LIB_CTX datatype OSSL_PARAM datatype From 567e2ae915e2a2d3a0cba489ab646af0ea95c225 Mon Sep 17 00:00:00 2001 From: Damian Hobson-Garcia Date: Fri, 5 Apr 2024 12:33:40 -0400 Subject: [PATCH 08/12] x509_acert: Add simple API tests Add a some simple API tests for reading, printing, signing and verifying attribute certificates. --- test/build.info | 7 +- test/certs/acert.pem | 5 + test/certs/acert_ietf.pem | 15 +++ test/recipes/60-test_x509_acert.t | 22 ++++ test/x509_acert_test.c | 174 ++++++++++++++++++++++++++++++ 5 files changed, 222 insertions(+), 1 deletion(-) create mode 100644 test/certs/acert.pem create mode 100644 test/certs/acert_ietf.pem create mode 100644 test/recipes/60-test_x509_acert.t create mode 100644 test/x509_acert_test.c diff --git a/test/build.info b/test/build.info index 52bc392f1206e..b51e0f895e785 100644 --- a/test/build.info +++ b/test/build.info @@ -63,7 +63,8 @@ IF[{- !$disabled{tests} -}] provfetchtest prov_config_test rand_test \ ca_internals_test bio_tfo_test membio_test bio_dgram_test list_test \ fips_version_test x509_test hpke_test pairwise_fail_test \ - nodefltctxtest evp_xof_test x509_load_cert_file_test bio_meth_test + nodefltctxtest evp_xof_test x509_load_cert_file_test bio_meth_test \ + x509_acert_test IF[{- !$disabled{'rpk'} -}] PROGRAMS{noinst}=rpktest @@ -1206,6 +1207,10 @@ ENDIF INCLUDE[cert_comp_test]=../include ../apps/include .. DEPEND[cert_comp_test]=../libcrypto ../libssl libtestutil.a + SOURCE[x509_acert_test]=x509_acert_test.c + INCLUDE[x509_acert_test]=../include ../apps/include + DEPEND[x509_acert_test]=../libcrypto libtestutil.a + {- use File::Spec::Functions; use File::Basename; diff --git a/test/certs/acert.pem b/test/certs/acert.pem new file mode 100644 index 0000000000000..efd2a05dc6e94 --- /dev/null +++ b/test/certs/acert.pem @@ -0,0 +1,5 @@ +Generated with paccor (https://github.com/nsacyber/paccor) + +-----BEGIN ATTRIBUTE CERTIFICATE----- +MIID4zCCAssCAQEwOaA3MB+kHTAbMRkwFwYDVQQDDBBUUE0gTWFudWZhY3R1cmVyAhRADHoGLYO7i9GfV2Yz2rrlRFDPSqA6MDikNjA0MQswCQYDVQQGEwJVUzEUMBIGA1UECgwLZXhhbXBsZS5jb20xDzANBgNVBAsMBlBDVGVzdDANBgkqhkiG9w0BAQsFAAIBATAiGA8yMDE4MDEwMTA1MDAwMFoYDzIwMjgwMTAxMDUwMDAwWjCB7TALBgVngQUCEzECMAAwHAYFZ4EFAhExEzARMAkCAQECAQMCARYEBAAAAAEwEgYFZ4EFAhkxCTAHBgVngQUIAjCBlQYHZ4EFBQEHAjGBiTCBhqBkMC8wDgYGZ4EFEgMBBAQAAgABDBJYWVogQ29tcHV0aW5nIEluYy4MATGABkFCQzEyMzAxMA4GBmeBBRIDAQQEAAcAAgwNTm90IFNwZWNpZmllZAwDSEQxgAgxMjM0QUJDRIMB/6IeMBwMCHVuYW1lIC1yDBA2LjUuMC0xNS1nZW5lcmljMBQGBWeBBQIXMQswCQIBAQIBAQIBETCCAScwbwYDVR0jBGgwZoAUl46DRCrPD3GZndkBbbNDngf6ZHChOKQ2MDQxCzAJBgNVBAYTAlVTMRQwEgYDVQQKDAtleGFtcGxlLmNvbTEPMA0GA1UECwwGUENUZXN0ghRmuv6Ey2JadCAOFysMNOn9CiH45zBBBgNVHSAEOjA4MDYGAioDMDAwLgYIKwYBBQUHAgIwIgwgVENHIFRydXN0ZWQgUGxhdGZvcm0gRW5kb3JzZW1lbnQwcQYDVR0RBGowaKRmMGQxEzARBgZngQUFAQQMB01vZGVsIEExHjAcBgZngQUFAQEMElhZWiBDb21wdXRpbmcgSW5jLjEZMBcGBmeBBQUBBQwNTm90IFNwZWNpZmllZDESMBAGBmeBBQUBBgwGQUJDMTIzMA0GCSqGSIb3DQEBCwUAA4IBAQBKj3TsMcVSu8zFLPwMX5f8SHelruSr0Oi4QCNLwAylo9Oi3loIaIwCRf95+z4SUJjGhyuBBooWP6Io0uWJ43tI7IFHdZ5vMtRIdemD3te+2iYH7OHMzMucsQ3L63n+XHHxhNrMgBOGI9+7Q4Rnvnm7OFFVqSKt5rX0sZFrR12XVD67CA3VC+khyXy1cV/3v95DwWFSHOG2VkiKjDdO2B4WRrK0Cy2J7ZFo0TYtaLNEONABL5nfa9MIXUAgo4hDwhxNSXfirJPgVt8HQNaoQPepHDiZbmRZc38Q1LAUpTxMYDePH0pQDkmI9Tdk+4/CMYhU0CIq6jzRHrOaUwfgNGp9 +-----END ATTRIBUTE CERTIFICATE----- diff --git a/test/certs/acert_ietf.pem b/test/certs/acert_ietf.pem new file mode 100644 index 0000000000000..fdb6e1114ebe2 --- /dev/null +++ b/test/certs/acert_ietf.pem @@ -0,0 +1,15 @@ +-----BEGIN ATTRIBUTE CERTIFICATE----- +MIICPTCCASUCAQEwN6AWMBGkDzANMQswCQYDVQQDDAJDQQIBAqEdpBswGTEXMBUG +A1UEAwwOc2VydmVyLmV4YW1wbGWgLTArpCkwJzElMCMGA1UEAwwcQXR0cmlidXRl +IENlcnRpZmljYXRlIElzc3VlcjANBgkqhkiG9w0BAQsFAAIUA7WQWQKiqrVAIUS4 +LE/ZgBtfV8IwIhgPMjAyMTA2MTUxMjM1MDBaGA8yMDMxMDYxMzEyMzUwMFowQTAj +BggrBgEFBQcKBDEXMBWgCYYHVGVzdHZhbDAIDAZncm91cDEwGgYDVQRIMRMwEaEP +gw1hZG1pbmlzdHJhdG9yMCwwHwYDVR0jBBgwFoAUYm7JaGdsZLtTgt0tqoCK2MrI +i10wCQYDVR04BAIFADANBgkqhkiG9w0BAQsFAAOCAQEARYpFEjordN68NXSLmDae +uruxhw+Zsr9Grom4mqYy/2lAuu58os4xA4Cez9OdkfCZmU9baDBVfMWys7GlCJdg +wn5uO5Kk2eiEWsujO/EV4c5eyXSrsUghQZQKfIVsgeut+7WfmDy/+j8ibbxJxJp/ +KMIoDjBAaMDhhxaCYclm8dJRT9DPba/bCNjuPGeTXslDQSXIfzFDgQkRZ0+Z7s9i +enYBmnH4rvW+SX8v5MzfGOu8VtHAKKBEcCbs2qGwYBEUUsCA/LYKSnOwn655wsd9 +k6KifIGOCVS4ZbK9pDyYcH/6/OmD1eSUwDZsd8CrJYDC31gAu7xqhOsBzR9DL3oD +4Q== +-----END ATTRIBUTE CERTIFICATE----- diff --git a/test/recipes/60-test_x509_acert.t b/test/recipes/60-test_x509_acert.t new file mode 100644 index 0000000000000..35864e6608137 --- /dev/null +++ b/test/recipes/60-test_x509_acert.t @@ -0,0 +1,22 @@ +#! /usr/bin/env perl +# Copyright 2021 The OpenSSL Project Authors. All Rights Reserved. +# +# Licensed under the Apache License 2.0 (the "License"). You may not use +# this file except in compliance with the License. You can obtain a copy +# in the file LICENSE in the source distribution or at +# https://www.openssl.org/source/license.html + +use OpenSSL::Test::Utils; +use OpenSSL::Test qw/:DEFAULT srctop_file/; + +setup("test_x509_acert"); + +plan skip_all => "test_x509_acert uses ec which is not supported by this build" + if disabled("ec"); + +plan tests => 1; + +ok(run(test(["x509_acert_test", srctop_file("test", "certs", "acert.pem"), + srctop_file("test", "certs", "acert_ietf.pem")])), + "running x509_acert_test"); + diff --git a/test/x509_acert_test.c b/test/x509_acert_test.c new file mode 100644 index 0000000000000..0099d4629e15b --- /dev/null +++ b/test/x509_acert_test.c @@ -0,0 +1,174 @@ +/* + * Copyright 2021 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include +#include +#include + +#include "testutil.h" + +static int test_print_acert(int idx) +{ + int ret = 0; + const char *acert_file; + X509_ACERT *acert = NULL; + BIO *bp, *bout; + + if (!TEST_ptr(acert_file = test_get_argument(idx))) + return 0; + + if (!TEST_ptr(bp = BIO_new_file(acert_file, "r"))) + return 0; + + if (!TEST_ptr(bout = BIO_new_fp(stderr, BIO_NOCLOSE))) + goto err; + + if (!TEST_ptr(acert = PEM_read_bio_X509_ACERT(bp, NULL, NULL, NULL))) + goto err; + + if (!TEST_int_eq(X509_ACERT_print(bout, acert), 1)) { + goto err; + } + + ret = 1; + +err: + BIO_free(bp); + BIO_free(bout); + X509_ACERT_free(acert); + return ret; +} + +static int test_acert_sign(void) +{ + int ret = 0; + const char *acert_file; + EVP_PKEY *pkey; + BIO *bp = NULL; + X509_ACERT *acert = NULL; + + if (!TEST_ptr(acert_file = test_get_argument(0))) + return 0; + + if (!TEST_ptr(pkey = EVP_RSA_gen(2048))) + return 0; + + if (!TEST_ptr(bp = BIO_new_file(acert_file, "r"))) + goto err; + + if (!TEST_ptr(acert = PEM_read_bio_X509_ACERT(bp, NULL, NULL, NULL))) + goto err; + + if (!TEST_int_gt(X509_ACERT_sign(acert, pkey, EVP_sha256()), 0) || + !TEST_int_eq(X509_ACERT_verify(acert, pkey), 1)) + goto err; + + ret = 1; + +err: + BIO_free(bp); + X509_ACERT_free(acert); + EVP_PKEY_free(pkey); + return ret; +} + +/* IetfAttrSyntax structure with one value */ +static const unsigned char attr_syntax_single[] = { + 0x30, 0x15, 0xa0, 0x09, 0x86, 0x07, 0x54, 0x65, 0x73, 0x74, 0x76, 0x61, + 0x6c, 0x30, 0x08, 0x0c, 0x06, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x31 +}; + +/* IetfAttrSyntax structure with multiple values of the same type */ +static const unsigned char attr_syntax_multiple[] = { + 0x30, 0x1d, 0x30, 0x1b, 0x0c, 0x07, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x20, + 0x31, 0x0c, 0x07, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x20, 0x32, 0x0c, 0x07, + 0x67, 0x72, 0x6f, 0x75, 0x70, 0x20, 0x33 +}; + +/* IetfAttrSyntax structure with multiple values of different types */ +static const unsigned char attr_syntax_diff_type[] = { + 0x30, 0x11, 0x30, 0x0f, 0x04, 0x08, 0x64, 0x65, 0x61, 0x64, 0x63, 0x6f, + 0x64, 0x65, 0x0c, 0x03, 0x61, 0x61, 0x61 +}; + +/* IetfAttrSyntax structure with an invalid/unsupported value type */ +static const unsigned char attr_syntax_invalid_type[] = { + 0x30, 0x05, 0x30, 0x03, 0x02, 0x01, 0x0a +}; + +#define ADD_TEST_DATA(x, valid) {x, sizeof(x), valid} + +struct ietf_type_test_data { + const unsigned char *data; + size_t len; + int valid; +}; + +static const struct ietf_type_test_data ietf_syntax_tests[] = { + ADD_TEST_DATA(attr_syntax_single, 1), + ADD_TEST_DATA(attr_syntax_multiple, 1), + ADD_TEST_DATA(attr_syntax_diff_type, 0), + ADD_TEST_DATA(attr_syntax_invalid_type, 0), +}; + +static int test_object_group_attr(int idx) +{ + int ret = 0; + OSSL_IETF_ATTR_SYNTAX *ias = NULL; + BIO *bout = NULL; + const unsigned char *p; + const struct ietf_type_test_data *test = &ietf_syntax_tests[idx]; + + if (!TEST_ptr(bout = BIO_new_fp(stderr, BIO_NOCLOSE))) + goto done; + + p = test->data; + + ias = d2i_OSSL_IETF_ATTR_SYNTAX(NULL, &p, test->len); + + if ((test->valid && !TEST_ptr(ias)) + || (!test->valid && !TEST_ptr_null(ias))) + goto done; + + if (ias != NULL + && !TEST_int_eq(OSSL_IETF_ATTR_SYNTAX_print(bout, ias, 4), 1)) { + OSSL_IETF_ATTR_SYNTAX_free(ias); + goto done; + } + + ret = 1; + +done: + OSSL_IETF_ATTR_SYNTAX_free(ias); + BIO_free(bout); + return ret; +} + +OPT_TEST_DECLARE_USAGE("[...]\n") +int setup_tests(void) +{ + int cnt; + + if (!test_skip_common_options()) { + TEST_error("Error parsing test options\n"); + return 0; + } + + cnt = test_get_argument_count(); + if (cnt < 1) { + TEST_error("Must specify at least 1 attribute certificate file\n"); + return 0; + } + + ADD_ALL_TESTS(test_print_acert, cnt); + ADD_TEST(test_acert_sign); + ADD_ALL_TESTS(test_object_group_attr, OSSL_NELEM(ietf_syntax_tests)); + + return 1; +} From 5814502b8425f4f10113ad0208808e1d5499547a Mon Sep 17 00:00:00 2001 From: Damian Hobson-Garcia Date: Tue, 4 Apr 2023 14:52:56 -0400 Subject: [PATCH 09/12] fuzz: Add attribute certificate fuzz test --- fuzz/acert.c | 48 +++++++++++++++++++++++++++++++ fuzz/build.info | 11 ++++++- fuzz/corpora | 2 +- test/recipes/99-test_fuzz_acert.t | 22 ++++++++++++++ 4 files changed, 81 insertions(+), 2 deletions(-) create mode 100644 fuzz/acert.c create mode 100644 test/recipes/99-test_fuzz_acert.t diff --git a/fuzz/acert.c b/fuzz/acert.c new file mode 100644 index 0000000000000..542e793f9fb46 --- /dev/null +++ b/fuzz/acert.c @@ -0,0 +1,48 @@ +/* + * Copyright 2023 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * https://www.openssl.org/source/license.html + * or in the file LICENSE in the source distribution. + */ + +#include +#include +#include +#include +#include "fuzzer.h" + +int FuzzerInitialize(int *argc, char ***argv) +{ + OPENSSL_init_crypto(OPENSSL_INIT_LOAD_CRYPTO_STRINGS, NULL); + ERR_clear_error(); + CRYPTO_free_ex_index(0, -1); + return 1; +} + +int FuzzerTestOneInput(const uint8_t *buf, size_t len) +{ + const unsigned char *p = buf; + unsigned char *der = NULL; + + X509_ACERT *acert = d2i_X509_ACERT(NULL, &p, len); + if (acert != NULL) { + BIO *bio = BIO_new(BIO_s_null()); + + X509_ACERT_print(bio, acert); + BIO_free(bio); + + i2d_X509_ACERT(acert, &der); + OPENSSL_free(der); + + X509_ACERT_free(acert); + } + ERR_clear_error(); + return 0; +} + +void FuzzerCleanup(void) +{ +} diff --git a/fuzz/build.info b/fuzz/build.info index f31178a52462b..21686f980c783 100644 --- a/fuzz/build.info +++ b/fuzz/build.info @@ -10,7 +10,7 @@ IF[{- !$disabled{"fuzz-afl"} || !$disabled{"fuzz-libfuzzer"} -}] PROGRAMS{noinst}=asn1 asn1parse bignum bndiv client conf crl server smime - PROGRAMS{noinst}=punycode pem decoder + PROGRAMS{noinst}=punycode pem decoder acert PROGRAMS{noinst}=v3name IF[{- !$disabled{"cmp"} -}] @@ -93,6 +93,10 @@ IF[{- !$disabled{"fuzz-afl"} || !$disabled{"fuzz-libfuzzer"} -}] INCLUDE[decoder]=../include {- $ex_inc -} DEPEND[decoder]=../libcrypto {- $ex_lib -} + SOURCE[acert]=acert.c driver.c + INCLUDE[acert]=../include {- $ex_inc -} + DEPEND[acert]=../libcrypto {- $ex_lib -} + SOURCE[punycode]=punycode.c driver.c INCLUDE[punycode]=../include {- $ex_inc -} DEPEND[punycode]=../libcrypto.a {- $ex_lib -} @@ -133,6 +137,7 @@ ENDIF IF[{- !$disabled{tests} -}] PROGRAMS{noinst}=asn1-test asn1parse-test bignum-test bndiv-test client-test conf-test crl-test server-test smime-test PROGRAMS{noinst}=punycode-test pem-test decoder-test + PROGRAMS{noinst}=punycode-test pem-test decoder-test acert-test PROGRAMS{noinst}=v3name-test IF[{- !$disabled{"cmp"} -}] @@ -217,6 +222,10 @@ IF[{- !$disabled{tests} -}] INCLUDE[decoder-test]=../include DEPEND[decoder-test]=../libcrypto + SOURCE[acert-test]=acert.c test-corpus.c + INCLUDE[acert-test]=../include + DEPEND[acert-test]=../libcrypto + SOURCE[punycode-test]=punycode.c test-corpus.c INCLUDE[punycode-test]=../include DEPEND[punycode-test]=../libcrypto.a diff --git a/fuzz/corpora b/fuzz/corpora index 3e7b9b6d1dfa2..9f7667061314e 160000 --- a/fuzz/corpora +++ b/fuzz/corpora @@ -1 +1 @@ -Subproject commit 3e7b9b6d1dfa28b54e8e4206be1bf59338c14c46 +Subproject commit 9f7667061314ecf9a287ce1c9702073ca1e345e3 diff --git a/test/recipes/99-test_fuzz_acert.t b/test/recipes/99-test_fuzz_acert.t new file mode 100644 index 0000000000000..f62be1ec69002 --- /dev/null +++ b/test/recipes/99-test_fuzz_acert.t @@ -0,0 +1,22 @@ +#!/usr/bin/env perl +# Copyright 2023 The OpenSSL Project Authors. All Rights Reserved. +# +# Licensed under the Apache License 2.0 (the "License"). You may not use +# this file except in compliance with the License. You can obtain a copy +# in the file LICENSE in the source distribution or at +# https://www.openssl.org/source/license.html + +use strict; +use warnings; + +use OpenSSL::Test qw/:DEFAULT srctop_file/; +use OpenSSL::Test::Utils; + +my $fuzzer = "acert"; +setup("test_fuzz_${fuzzer}"); + +plan tests => 2; # one more due to below require_ok(...) + +require_ok(srctop_file('test','recipes','fuzz.pl')); + +fuzz_ok($fuzzer); From a3db7a9d44c808080d35fbf1620539115eebb0b4 Mon Sep 17 00:00:00 2001 From: Damian Hobson-Garcia Date: Thu, 3 Jun 2021 15:41:27 +0900 Subject: [PATCH 10/12] x509_acert: Load attributes from config file section Several of the attribute values defined for use by attribute certificates use multi-valued data in an ASN.1 SEQUENCE. Allow reading of these values from a configuration file, similar to how generic X.509 extensions are handled. --- crypto/x509/x509_acert.c | 68 ++++++++++++++++++++++++++ doc/build.info | 6 +++ doc/man3/X509_ACERT_add_attr_nconf.pod | 63 ++++++++++++++++++++++++ include/openssl/x509_acert.h.in | 2 + util/libcrypto.num | 1 + 5 files changed, 140 insertions(+) create mode 100644 doc/man3/X509_ACERT_add_attr_nconf.pod diff --git a/crypto/x509/x509_acert.c b/crypto/x509/x509_acert.c index 9499a14cc0bb1..a3c894636339e 100644 --- a/crypto/x509/x509_acert.c +++ b/crypto/x509/x509_acert.c @@ -7,7 +7,10 @@ * https://www.openssl.org/source/license.html */ +#include +#include #include +#include #include #include #include "x509_acert.h" @@ -243,6 +246,71 @@ int X509_ACERT_add1_attr_by_txt(X509_ACERT *x, const char *attrname, int type, return X509at_add1_attr_by_txt(attrs, attrname, type, bytes, len) != NULL; } +static int check_asn1_attribute(const char **value) +{ + const char *p = *value; + + if (strncmp(p, "ASN1:", 5) != 0) + return 0; + + p += 5; + while (ossl_isspace(*p)) + p++; + + *value = p; + return 1; +} + +int X509_ACERT_add_attr_nconf(CONF *conf, const char *section, + X509_ACERT *acert) +{ + int ret = 0, i; + STACK_OF(CONF_VALUE) *attr_sk = NCONF_get_section(conf, section); + + if (attr_sk == NULL) + goto err; + + for (i = 0; i < sk_CONF_VALUE_num(attr_sk); i++) { + CONF_VALUE *v = sk_CONF_VALUE_value(attr_sk, i); + const char *value = v->value; + + if (value == NULL) { + ERR_raise_data(ERR_LIB_X509, X509_R_INVALID_ATTRIBUTES, + "name=%s,section=%s",v->name, section); + goto err; + } + + if (check_asn1_attribute(&value) == 1) { + int att_len; + unsigned char *att_data = NULL; + ASN1_TYPE *asn1 = ASN1_generate_nconf(value, conf); + + if (asn1 == NULL) + goto err; + + att_len = i2d_ASN1_TYPE(asn1, &att_data); + + ret = X509_ACERT_add1_attr_by_txt(acert, v->name, V_ASN1_SEQUENCE, + att_data, att_len); + OPENSSL_free(att_data); + ASN1_TYPE_free(asn1); + + if (!ret) + goto err; + } else { + ret = X509_ACERT_add1_attr_by_txt(acert, v->name, + V_ASN1_OCTET_STRING, + (unsigned char *)value, + strlen(value)); + if (!ret) + goto err; + } + } + ret = 1; +err: + return ret; +} + void *X509_ACERT_get_ext_d2i(const X509_ACERT *x, int nid, int *crit, int *idx) { return X509V3_get_d2i(x->acinfo->extensions, nid, crit, idx); diff --git a/doc/build.info b/doc/build.info index 373f61476e348..c7cb6d5d4fb3e 100644 --- a/doc/build.info +++ b/doc/build.info @@ -2811,6 +2811,10 @@ DEPEND[html/man3/X509_ACERT_add1_attr.html]=man3/X509_ACERT_add1_attr.pod GENERATE[html/man3/X509_ACERT_add1_attr.html]=man3/X509_ACERT_add1_attr.pod DEPEND[man/man3/X509_ACERT_add1_attr.3]=man3/X509_ACERT_add1_attr.pod GENERATE[man/man3/X509_ACERT_add1_attr.3]=man3/X509_ACERT_add1_attr.pod +DEPEND[html/man3/X509_ACERT_add_attr_nconf.html]=man3/X509_ACERT_add_attr_nconf.pod +GENERATE[html/man3/X509_ACERT_add_attr_nconf.html]=man3/X509_ACERT_add_attr_nconf.pod +DEPEND[man/man3/X509_ACERT_add_attr_nconf.3]=man3/X509_ACERT_add_attr_nconf.pod +GENERATE[man/man3/X509_ACERT_add_attr_nconf.3]=man3/X509_ACERT_add_attr_nconf.pod DEPEND[html/man3/X509_ACERT_get0_holder_baseCertId.html]=man3/X509_ACERT_get0_holder_baseCertId.pod GENERATE[html/man3/X509_ACERT_get0_holder_baseCertId.html]=man3/X509_ACERT_get0_holder_baseCertId.pod DEPEND[man/man3/X509_ACERT_get0_holder_baseCertId.3]=man3/X509_ACERT_get0_holder_baseCertId.pod @@ -3658,6 +3662,7 @@ html/man3/UI_new.html \ html/man3/X509V3_get_d2i.html \ html/man3/X509V3_set_ctx.html \ html/man3/X509_ACERT_add1_attr.html \ +html/man3/X509_ACERT_add_attr_nconf.html \ html/man3/X509_ACERT_get0_holder_baseCertId.html \ html/man3/X509_ACERT_get_attr.html \ html/man3/X509_ACERT_print_ex.html \ @@ -4309,6 +4314,7 @@ man/man3/UI_new.3 \ man/man3/X509V3_get_d2i.3 \ man/man3/X509V3_set_ctx.3 \ man/man3/X509_ACERT_add1_attr.3 \ +man/man3/X509_ACERT_add_attr_nconf.3 \ man/man3/X509_ACERT_get0_holder_baseCertId.3 \ man/man3/X509_ACERT_get_attr.3 \ man/man3/X509_ACERT_print_ex.3 \ diff --git a/doc/man3/X509_ACERT_add_attr_nconf.pod b/doc/man3/X509_ACERT_add_attr_nconf.pod new file mode 100644 index 0000000000000..a16d31c3f3698 --- /dev/null +++ b/doc/man3/X509_ACERT_add_attr_nconf.pod @@ -0,0 +1,63 @@ +=pod + +=head1 NAME + +X509_ACERT_add_attr_nconf +- Add attributes to X509_ACERT from configuration section + +=head1 SYNOPSIS + + #include + + int X509_ACERT_add_attr_nconf(CONF *conf, const char *section, + X509_ACERT *acert); + +=head1 DESCRIPTION + +X509_ACERT_add_attr_nconf() adds one or more Bs to the +existing B structure I. The attributes are read +from a I
of the I object. + +The give I
of the configuration should contain attribute +descriptions of the form: + + attribute_name = value + +The format of B will vary depending on the B. +B can either be a string value or an B +object. + +To encode an B object, use the prefix "ASN1:" followed by +the object description that uses the same syntax as L. +For example: + + id-aca-group = ASN1:SEQUENCE:ietfattr + + [ietfattr] + values = SEQUENCE:groups + + [groups] + 1.string = UTF8:mygroup1 + +=head1 RETURN VALUES + +X509_ACERT_add_attr_nconf() returns 1 for success and 0 for failure. + +=head1 SEE ALSO + +L. + +=head1 HISTORY + +The function X509_ACERT_add_attr_nconf() was added in OpenSSL 3.4. + +=head1 COPYRIGHT + +Copyright 2023 The OpenSSL Project Authors. All Rights Reserved. + +Licensed under the Apache License 2.0 (the "License"). You may not use +this file except in compliance with the License. You can obtain a copy +in the file LICENSE in the source distribution or at +L. + +=cut diff --git a/include/openssl/x509_acert.h.in b/include/openssl/x509_acert.h.in index a1e6704c0d92f..42376a6cb763e 100644 --- a/include/openssl/x509_acert.h.in +++ b/include/openssl/x509_acert.h.in @@ -98,6 +98,8 @@ int X509_ACERT_add1_attr_by_NID(X509_ACERT *x, int nid, int type, const void *bytes, int len); int X509_ACERT_add1_attr_by_txt(X509_ACERT *x, const char *attrname, int type, const unsigned char *bytes, int len); +int X509_ACERT_add_attr_nconf(CONF *conf, const char *section, + X509_ACERT *acert); int X509_ACERT_set1_issuerName(X509_ACERT *x, const X509_NAME *name); int X509_ACERT_set1_serialNumber(X509_ACERT *x, const ASN1_INTEGER *serial); diff --git a/util/libcrypto.num b/util/libcrypto.num index ae11b7b02385c..903ca8f899ef6 100644 --- a/util/libcrypto.num +++ b/util/libcrypto.num @@ -5630,3 +5630,4 @@ OSSL_IETF_ATTR_SYNTAX_get_value_num ? 3_4_0 EXIST::FUNCTION: OSSL_IETF_ATTR_SYNTAX_get0_value ? 3_4_0 EXIST::FUNCTION: OSSL_IETF_ATTR_SYNTAX_add1_value ? 3_4_0 EXIST::FUNCTION: OSSL_IETF_ATTR_SYNTAX_print ? 3_4_0 EXIST::FUNCTION: +X509_ACERT_add_attr_nconf ? 3_4_0 EXIST::FUNCTION: From b4c471d2e072cab97f3b85284774eff27cdb52e8 Mon Sep 17 00:00:00 2001 From: Damian Hobson-Garcia Date: Mon, 26 Feb 2024 22:50:46 -0500 Subject: [PATCH 11/12] x509_acert: Add more parsing and printing tests These have been extracted from the boucycastle test code. Make sure that these certificates can be safely and correctly parsed and printed. --- test/certs/acert_bc1.pem | 46 +++++++++++++++++++++++++++++++ test/certs/acert_bc2.pem | 17 ++++++++++++ test/recipes/60-test_x509_acert.t | 4 ++- 3 files changed, 66 insertions(+), 1 deletion(-) create mode 100644 test/certs/acert_bc1.pem create mode 100644 test/certs/acert_bc2.pem diff --git a/test/certs/acert_bc1.pem b/test/certs/acert_bc1.pem new file mode 100644 index 0000000000000..78e25d65f0ef1 --- /dev/null +++ b/test/certs/acert_bc1.pem @@ -0,0 +1,46 @@ +Extracted from bouncycastle test case +Source: pkix/src/test/j2me/org/bouncycastle/cert/test/AttrCertTest.java +Copyright (c) 2000-2023 The Legion of the Bouncy Castle Inc. (https://www.bouncycastle.org) +SPDX-License-Identifier: MIT + +-----BEGIN ATTRIBUTE CERTIFICATE----- +MIIHQDCCBqkCAQEwgZChgY2kgYowgYcxHDAaBgkqhkiG9w0BCQEWDW1sb3JjaEB2 +dC5lZHUxHjAcBgNVBAMTFU1hcmt1cyBMb3JjaCAobWxvcmNoKTEbMBkGA1UECxMS +VmlyZ2luaWEgVGVjaCBVc2VyMRAwDgYDVQQLEwdDbGFzcyAyMQswCQYDVQQKEwJ2 +dDELMAkGA1UEBhMCVVMwgYmkgYYwgYMxGzAZBgkqhkiG9w0BCQEWDHNzaGFoQHZ0 +LmVkdTEbMBkGA1UEAxMSU3VtaXQgU2hhaCAoc3NoYWgpMRswGQYDVQQLExJWaXJn +aW5pYSBUZWNoIFVzZXIxEDAOBgNVBAsTB0NsYXNzIDExCzAJBgNVBAoTAnZ0MQsw +CQYDVQQGEwJVUzANBgkqhkiG9w0BAQQFAAIBBTAiGA8yMDAzMDcxODE2MDgwMloY +DzIwMDMwNzI1MTYwODAyWjCCBU0wggVJBgorBgEEAbRoCAEBMYIFORaCBTU8UnVs +ZSBSdWxlSWQ9IkZpbGUtUHJpdmlsZWdlLVJ1bGUiIEVmZmVjdD0iUGVybWl0Ij4K +IDxUYXJnZXQ+CiAgPFN1YmplY3RzPgogICA8U3ViamVjdD4KICAgIDxTdWJqZWN0 +TWF0Y2ggTWF0Y2hJZD0idXJuOm9hc2lzOm5hbWVzOnRjOnhhY21sOjEuMDpmdW5j +dGlvbjpzdHJpbmctZXF1YWwiPgogICAgIDxBdHRyaWJ1dGVWYWx1ZSBEYXRhVHlw +ZT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEjc3RyaW5nIj4KICAg +ICAgIENOPU1hcmt1cyBMb3JjaDwvQXR0cmlidXRlVmFsdWU+CiAgICAgPFN1Ympl +Y3RBdHRyaWJ1dGVEZXNpZ25hdG9yIEF0dHJpYnV0ZUlkPSJ1cm46b2FzaXM6bmFt +ZXM6dGM6eGFjbWw6MS4wOnN1YmplY3Q6c3ViamVjdC1pZCIgRGF0YVR5cGU9Imh0 +dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hI3N0cmluZyIgLz4gCiAgICA8 +L1N1YmplY3RNYXRjaD4KICAgPC9TdWJqZWN0PgogIDwvU3ViamVjdHM+CiAgPFJl +c291cmNlcz4KICAgPFJlc291cmNlPgogICAgPFJlc291cmNlTWF0Y2ggTWF0Y2hJ +ZD0idXJuOm9hc2lzOm5hbWVzOnRjOnhhY21sOjEuMDpmdW5jdGlvbjpzdHJpbmct +ZXF1YWwiPgogICAgIDxBdHRyaWJ1dGVWYWx1ZSBEYXRhVHlwZT0iaHR0cDovL3d3 +dy53My5vcmcvMjAwMS9YTUxTY2hlbWEjYW55VVJJIj4KICAgICAgaHR0cDovL3p1 +bmkuY3MudnQuZWR1PC9BdHRyaWJ1dGVWYWx1ZT4KICAgICA8UmVzb3VyY2VBdHRy +aWJ1dGVEZXNpZ25hdG9yIEF0dHJpYnV0ZUlkPSJ1cm46b2FzaXM6bmFtZXM6dGM6 +eGFjbWw6MS4wOnJlc291cmNlOnJlc291cmNlLWlkIiBEYXRhVHlwZT0iaHR0cDov +L3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEjYW55VVJJIiAvPiAKICAgIDwvUmVz +b3VyY2VNYXRjaD4KICAgPC9SZXNvdXJjZT4KICA8L1Jlc291cmNlcz4KICA8QWN0 +aW9ucz4KICAgPEFjdGlvbj4KICAgIDxBY3Rpb25NYXRjaCBNYXRjaElkPSJ1cm46 +b2FzaXM6bmFtZXM6dGM6eGFjbWw6MS4wOmZ1bmN0aW9uOnN0cmluZy1lcXVhbCI+ +CiAgICAgPEF0dHJpYnV0ZVZhbHVlIERhdGFUeXBlPSJodHRwOi8vd3d3LnczLm9y +Zy8yMDAxL1hNTFNjaGVtYSNzdHJpbmciPgpEZWxlZ2F0ZSBBY2Nlc3MgICAgIDwv +QXR0cmlidXRlVmFsdWU+CgkgIDxBY3Rpb25BdHRyaWJ1dGVEZXNpZ25hdG9yIEF0 +dHJpYnV0ZUlkPSJ1cm46b2FzaXM6bmFtZXM6dGM6eGFjbWw6MS4wOmFjdGlvbjph +Y3Rpb24taWQiIERhdGFUeXBlPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNj +aGVtYSNzdHJpbmciIC8+IAogICAgPC9BY3Rpb25NYXRjaD4KICAgPC9BY3Rpb24+ +CiAgPC9BY3Rpb25zPgogPC9UYXJnZXQ+CjwvUnVsZT4KMA0GCSqGSIb3DQEBBAUA +A4GBAGiJSM48XsY90HlYxGmGVSmNR6ZW2As+bot3KAfiCIkUIOAqhcphBS23egTr +6asYwy151HshbPNYz+Cgeqs45KkVzh7bL/0e1r8sDVIaaGIkjHK3CqBABnfSayr3 +Rd1yBoDdEv8Qb+3eEPH6ab9021AsLEnJ6LWTmybbOpMNZ3tv +-----END ATTRIBUTE CERTIFICATE----- diff --git a/test/certs/acert_bc2.pem b/test/certs/acert_bc2.pem new file mode 100644 index 0000000000000..4276949f14619 --- /dev/null +++ b/test/certs/acert_bc2.pem @@ -0,0 +1,17 @@ +Extracted from bouncycastle test case +Source: pkix/src/test/java/org/bouncycastle/cert/test/PEMData.java +Copyright (c) 2000-2023 The Legion of the Bouncy Castle Inc. (https://www.bouncycastle.org) +SPDX-License-Identifier: MIT + +-----BEGIN ATTRIBUTE CERTIFICATE----- +MIIBuDCCASECAQEwZ6BlMGCkXjBcMQswCQYDVQQGEwJBVTEoMCYGA1UEChMfVGhl +IExlZ2lvbiBvZiB0aGUgQm91bmN5IENhc3RsZTEjMCEGA1UECxMaQm91bmN5IFBy +aW1hcnkgQ2VydGlmaWNhdGUCARSgYjBgpF4wXDELMAkGA1UEBhMCQVUxKDAmBgNV +BAoTH1RoZSBMZWdpb24gb2YgdGhlIEJvdW5jeSBDYXN0bGUxIzAhBgNVBAsTGkJv +dW5jeSBQcmltYXJ5IENlcnRpZmljYXRlMA0GCSqGSIb3DQEBBQUAAgEBMCIYDzIw +MDUwNjEwMDI0MTMzWhgPMjAwNTA2MTAwMjQzMTNaMBkwFwYDVRhIMRAwDoEMREFV +MTIzNDU2Nzg5MA0GCSqGSIb3DQEBBQUAA4GBALAYXT9zdxSR5zdPLAon1xIPehgI +NZhjM7w0uu3OdzSV5sC31X1Kx9vi5RIWiM9VimRTwbQIod9POttD5QMXCwQb/fm7 +eiJqL2YBIXOeClB19VrQe8xQtMFbyuFpDiM7QdvIam9ShZZMEMGjv9QHI64M4b0G +odUBlSsJwPPQjZSU +-----END ATTRIBUTE CERTIFICATE----- diff --git a/test/recipes/60-test_x509_acert.t b/test/recipes/60-test_x509_acert.t index 35864e6608137..2bf58d6a6f75a 100644 --- a/test/recipes/60-test_x509_acert.t +++ b/test/recipes/60-test_x509_acert.t @@ -17,6 +17,8 @@ plan skip_all => "test_x509_acert uses ec which is not supported by this build" plan tests => 1; ok(run(test(["x509_acert_test", srctop_file("test", "certs", "acert.pem"), - srctop_file("test", "certs", "acert_ietf.pem")])), + srctop_file("test", "certs", "acert_ietf.pem"), + srctop_file("test", "certs", "acert_bc1.pem"), + srctop_file("test", "certs", "acert_bc2.pem")])), "running x509_acert_test"); From 09066b1e181144c68fcc7ed3cbcace581ab1948c Mon Sep 17 00:00:00 2001 From: Damian Hobson-Garcia Date: Mon, 2 Oct 2023 17:32:25 -0400 Subject: [PATCH 12/12] Add Attribute Certificate suport comments to CHANGES and NEWS --- CHANGES.md | 6 ++++++ NEWS.md | 2 ++ 2 files changed, 8 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index aaa47976a2b99..28e1c0ae4d88f 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -47,6 +47,12 @@ OpenSSL 3.4 *Tim Perry* + * Added Attribute Certificate (RFC 5755) support. Attribute + Certificates can be created, parsed, modified and printed via the + public API. There is no command-line tool support at this time. + + *Damian Hobson-Garcia* + OpenSSL 3.3 ----------- diff --git a/NEWS.md b/NEWS.md index 7af4eabb2c3d3..3196a06254184 100644 --- a/NEWS.md +++ b/NEWS.md @@ -28,6 +28,8 @@ OpenSSL. This release is in development. + * Added initial Attribute Certificate (RFC 5755) support. + OpenSSL 3.3 -----------