Added tests for SDA and AI extensions.
Added internal function ossl_print_attribute_value() with documentation.
Reviewed-by: Neil Horman <nhorman@openssl.org>
Reviewed-by: Tomas Mraz <tomas@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/24669)
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 x_ietfatt.c v3_ac_tgt.c
+ x509_acert.c x509aset.c t_acert.c x_ietfatt.c v3_ac_tgt.c v3_sda.c
IF[{- !$disabled{'deprecated-3.0'} -}]
SOURCE[../../libcrypto]=x509type.c
extern const X509V3_EXT_METHOD ossl_v3_targeting_information;
extern const X509V3_EXT_METHOD ossl_v3_holder_name_constraints;
extern const X509V3_EXT_METHOD ossl_v3_delegated_name_constraints;
+extern const X509V3_EXT_METHOD ossl_v3_subj_dir_attrs;
+extern const X509V3_EXT_METHOD ossl_v3_associated_info;
&ossl_v3_name_constraints,
&ossl_v3_policy_mappings,
&ossl_v3_inhibit_anyp,
+ &ossl_v3_subj_dir_attrs,
&ossl_v3_idp,
&ossl_v3_alt[2],
&ossl_v3_freshest_crl,
&ossl_v3_single_use,
&ossl_v3_group_ac,
&ossl_v3_holder_name_constraints,
+ &ossl_v3_associated_info,
};
/* Number of standard extensions */
--- /dev/null
+/*
+ * Copyright 2024 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 <openssl/asn1t.h>
+#include <openssl/x509v3.h>
+#include <crypto/x509.h>
+#include "ext_dat.h"
+
+ASN1_ITEM_TEMPLATE(ATTRIBUTES_SYNTAX) =
+ ASN1_EX_TEMPLATE_TYPE(ASN1_TFLG_SEQUENCE_OF, 0, Attributes, X509_ATTRIBUTE)
+ASN1_ITEM_TEMPLATE_END(ATTRIBUTES_SYNTAX)
+
+IMPLEMENT_ASN1_FUNCTIONS(ATTRIBUTES_SYNTAX)
+
+static int i2r_ATTRIBUTES_SYNTAX(X509V3_EXT_METHOD *method,
+ ATTRIBUTES_SYNTAX *attrlst,
+ BIO *out, int indent)
+{
+ X509_ATTRIBUTE *attr;
+ ASN1_TYPE *av;
+ int i, j, attr_nid;
+
+ if (!attrlst) {
+ if (BIO_printf(out, "<No Attributes>\n") <= 0)
+ return 0;
+ return 1;
+ }
+ if (!sk_X509_ATTRIBUTE_num(attrlst)) {
+ if (BIO_printf(out, "<Empty Attributes>\n") <= 0)
+ return 0;
+ return 1;
+ }
+
+ for (i = 0; i < sk_X509_ATTRIBUTE_num(attrlst); i++) {
+ ASN1_OBJECT *attr_obj;
+ attr = sk_X509_ATTRIBUTE_value(attrlst, i);
+ attr_obj = X509_ATTRIBUTE_get0_object(attr);
+ attr_nid = OBJ_obj2nid(attr_obj);
+ if (indent && BIO_printf(out, "%*s", indent, "") <= 0)
+ return 0;
+ if (attr_nid == NID_undef) {
+ if (i2a_ASN1_OBJECT(out, attr_obj) <= 0)
+ return 0;
+ if (BIO_puts(out, ":\n") <= 0)
+ return 0;
+ } else if (BIO_printf(out, "%s:\n", OBJ_nid2ln(attr_nid)) <= 0) {
+ return 0;
+ }
+
+ if (X509_ATTRIBUTE_count(attr)) {
+ for (j = 0; j < X509_ATTRIBUTE_count(attr); j++)
+ {
+ av = X509_ATTRIBUTE_get0_type(attr, j);
+ if (ossl_print_attribute_value(out, attr_nid, av, indent + 4) <= 0)
+ return 0;
+ if (BIO_puts(out, "\n") <= 0)
+ return 0;
+ }
+ } else if (BIO_printf(out, "%*s<No Values>\n", indent + 4, "") <= 0) {
+ return 0;
+ }
+ }
+ return 1;
+}
+
+const X509V3_EXT_METHOD ossl_v3_subj_dir_attrs = {
+ NID_subject_directory_attributes, X509V3_EXT_MULTILINE,
+ ASN1_ITEM_ref(ATTRIBUTES_SYNTAX),
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ (X509V3_EXT_I2R)i2r_ATTRIBUTES_SYNTAX,
+ 0,
+ NULL
+};
+
+const X509V3_EXT_METHOD ossl_v3_associated_info = {
+ NID_associated_information, X509V3_EXT_MULTILINE,
+ ASN1_ITEM_ref(ATTRIBUTES_SYNTAX),
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ (X509V3_EXT_I2R)i2r_ATTRIBUTES_SYNTAX,
+ 0,
+ NULL
+};
#include <openssl/asn1t.h>
#include <openssl/x509.h>
#include "x509_local.h"
+#include <crypto/x509.h>
/*-
* X509_ATTRIBUTE: this has the following form:
ASN1_TYPE_free(val);
return NULL;
}
+
+static int print_hex(BIO *out, unsigned char *buf, int len)
+{
+ int i;
+
+ for (i = 0; i < len; i++) {
+ if (BIO_printf(out, "%02X ", buf[i]) <= 0) {
+ return 0;
+ }
+ }
+ return 1;
+}
+
+static int asn1_integer_print_bio(BIO *bio, const ASN1_INTEGER *num)
+{
+ BIGNUM *num_bn;
+ int result = 0;
+ char *hex;
+
+ num_bn = ASN1_INTEGER_to_BN(num, NULL);
+ if (num_bn == NULL)
+ return -1;
+ if ((hex = BN_bn2hex(num_bn)) != NULL) {
+ result = BIO_write(bio, "0x", 2) > 0;
+ result = result && BIO_write(bio, hex, strlen(hex)) > 0;
+ OPENSSL_free(hex);
+ } else {
+ return -1;
+ }
+ BN_free(num_bn);
+
+ return result;
+}
+
+static int print_oid (BIO *out, ASN1_OBJECT *oid) {
+ const char *ln;
+ char objbuf[80];
+ int rc;
+
+ if (OBJ_obj2txt(objbuf, sizeof(objbuf), oid, 1) <= 0)
+ return 0;
+ ln = OBJ_nid2ln(OBJ_obj2nid(oid));
+ rc = (ln != NULL)
+ ? BIO_printf(out, "%s (%s)", objbuf, ln)
+ : BIO_printf(out, "%s", objbuf);
+ if (rc < 0)
+ return 0;
+ return 1;
+}
+
+int ossl_print_attribute_value(BIO *out,
+ int obj_nid,
+ const ASN1_TYPE *av,
+ int indent)
+{
+ ASN1_STRING *str;
+ unsigned char *value;
+ X509_NAME *xn = NULL;
+ int64_t int_val;
+
+ /*
+ * This switch-case is only for syntaxes that are not encoded as a single
+ * primitively-constructed value universal ASN.1 type.
+ */
+ switch (obj_nid) {
+ case NID_undef: /* Unrecognized OID. */
+ break;
+ /* Attribute types with DN syntax. */
+ case NID_member:
+ case NID_roleOccupant:
+ case NID_seeAlso:
+ case NID_manager:
+ case NID_documentAuthor:
+ case NID_secretary:
+ case NID_associatedName:
+ case NID_dITRedirect:
+ case NID_owner:
+ value = av->value.sequence->data;
+ xn = d2i_X509_NAME(NULL,
+ (const unsigned char**)&(av->value.sequence->data),
+ av->value.sequence->length);
+ if (xn == NULL) {
+ BIO_puts(out, "(COULD NOT DECODE DISTINGUISHED NAME)\n");
+ return 0;
+ }
+ /*
+ * d2i_ functions increment the ppin pointer. See doc/man3/d2i_X509.pod.
+ * This resets the pointer. We don't want to corrupt this value.
+ */
+ av->value.sequence->data = value;
+ if (X509_NAME_print_ex(out, xn, indent, XN_FLAG_SEP_CPLUS_SPC) <= 0)
+ return 0;
+ X509_NAME_free(xn);
+ return 1;
+
+ default:
+ break;
+ }
+
+ switch (av->type) {
+ case V_ASN1_BOOLEAN:
+ if (av->value.boolean) {
+ return BIO_printf(out, "%*sTRUE", indent, "");
+ } else {
+ return BIO_printf(out, "%*sFALSE", indent, "");
+ }
+
+ case V_ASN1_INTEGER:
+ if (BIO_printf(out, "%*s", indent, "") <= 0)
+ return 0;
+ if (ASN1_INTEGER_get_int64(&int_val, av->value.integer) > 0) {
+ return BIO_printf(out, "%lld", (long long int)int_val);
+ } else {
+ str = av->value.integer;
+ return asn1_integer_print_bio(out, str);
+ }
+
+ case V_ASN1_ENUMERATED:
+ if (BIO_printf(out, "%*s", indent, "") <= 0)
+ return 0;
+ if (ASN1_ENUMERATED_get_int64(&int_val, av->value.enumerated) > 0) {
+ return BIO_printf(out, "%lld", (long long int)int_val);
+ } else {
+ str = av->value.enumerated;
+ return asn1_integer_print_bio(out, str);
+ }
+
+ case V_ASN1_BIT_STRING:
+ if (BIO_printf(out, "%*s", indent, "") <= 0)
+ return 0;
+ return print_hex(out, av->value.bit_string->data,
+ av->value.bit_string->length);
+
+ case V_ASN1_OCTET_STRING:
+ case V_ASN1_VIDEOTEXSTRING:
+ if (BIO_printf(out, "%*s", indent, "") <= 0)
+ return 0;
+ return print_hex(out, av->value.octet_string->data,
+ av->value.octet_string->length);
+
+ case V_ASN1_NULL:
+ return BIO_printf(out, "%*sNULL", indent, "");
+
+ case V_ASN1_OBJECT:
+ if (BIO_printf(out, "%*s", indent, "") <= 0)
+ return 0;
+ return print_oid(out, av->value.object);
+
+ /*
+ * ObjectDescriptor is an IMPLICIT GraphicString, but GeneralString is a
+ * superset supported by OpenSSL, so we will use that anywhere a
+ * GraphicString is needed here.
+ */
+ case V_ASN1_GENERALSTRING:
+ case V_ASN1_GRAPHICSTRING:
+ case V_ASN1_OBJECT_DESCRIPTOR:
+ return BIO_printf(out, "%*s%.*s", indent, "",
+ av->value.generalstring->length,
+ av->value.generalstring->data);
+
+ /* EXTERNAL would go here. */
+ /* EMBEDDED PDV would go here. */
+
+ case V_ASN1_UTF8STRING:
+ return BIO_printf(out, "%*s%.*s", indent, "",
+ av->value.utf8string->length,
+ av->value.utf8string->data);
+
+ case V_ASN1_REAL:
+ return BIO_printf(out, "%*sREAL", indent, "");
+
+ /* RELATIVE-OID would go here. */
+ /* TIME would go here. */
+
+ case V_ASN1_SEQUENCE:
+ return ASN1_parse_dump(out, av->value.sequence->data,
+ av->value.sequence->length, indent, 1);
+
+ case V_ASN1_SET:
+ return ASN1_parse_dump(out, av->value.set->data,
+ av->value.set->length, indent, 1);
+
+ /*
+ * UTCTime ::= [UNIVERSAL 23] IMPLICIT VisibleString
+ * GeneralizedTime ::= [UNIVERSAL 24] IMPLICIT VisibleString
+ * VisibleString is a superset for NumericString, so it will work for that.
+ */
+ case V_ASN1_VISIBLESTRING:
+ case V_ASN1_UTCTIME:
+ case V_ASN1_GENERALIZEDTIME:
+ case V_ASN1_NUMERICSTRING:
+ return BIO_printf(out, "%*s%.*s", indent, "",
+ av->value.visiblestring->length,
+ av->value.visiblestring->data);
+
+ case V_ASN1_PRINTABLESTRING:
+ return BIO_printf(out, "%*s%.*s", indent, "",
+ av->value.printablestring->length,
+ av->value.printablestring->data);
+
+ case V_ASN1_T61STRING:
+ return BIO_printf(out, "%*s%.*s", indent, "",
+ av->value.t61string->length,
+ av->value.t61string->data);
+
+ case V_ASN1_IA5STRING:
+ return BIO_printf(out, "%*s%.*s", indent, "",
+ av->value.ia5string->length,
+ av->value.ia5string->data);
+
+ /* UniversalString would go here. */
+ /* CHARACTER STRING would go here. */
+ /* BMPString would go here. */
+ /* DATE would go here. */
+ /* TIME-OF-DAY would go here. */
+ /* DATE-TIME would go here. */
+ /* DURATION would go here. */
+ /* OID-IRI would go here. */
+ /* RELATIVE-OID-IRI would go here. */
+
+ /* Would it be approriate to just hexdump? */
+ default:
+ return BIO_printf(out, "%*s<Unsupported tag %d>", indent, "", av->type);
+ }
+}
--- /dev/null
+=pod
+
+=head1 NAME
+
+ossl_print_attribute_value
+- Print an X.500 directory attribute value
+
+=head1 SYNOPSIS
+
+ #include <crypto/x509.h>
+
+ int ossl_print_attribute_value(BIO *out, int obj_nid, const ASN1_TYPE *av, int indent);
+
+=head1 DESCRIPTION
+
+ossl_print_attribute_value() prints an X.500 directory value, which is an
+ASN.1 value and an associated attribute type that informs its interpretation,
+syntax, display characteristics, comparison, sorting, and substring searching
+behaviors, among other things. This attribute type is identified by an ASN.1
+object identifier.
+
+X.500 directory values are used in the relative distinguished names in a
+distinguished name, as seen in the C<subject> and C<issuer> fields of an X.509
+public key certificate. They also appear in the attributes of an X.509
+attribute certificate, as well as in the subjectDirectoryAttributes or
+associatedInformation X.509v3 extensions.
+
+The I<out> argument is a B<BIO> pointer for printing the output. The I<obj_nid>
+argument is the NID of the attribute type object identifier. The ASN.1 value
+itself is passed in I<av> and the level of desired indentation in terms of the
+number of spaces is specified in I<indent>.
+
+This function generally prints values in such a way as to keep them on a single
+line, but this is not always the case. Unrecognized attribute types whose syntax
+is a C<SET> or C<SEQUENCE> will be printed on multiple lines, for instance. Not
+all ASN.1 syntaxes are currently supported, and there is no guarantee for what
+printed values will look like in future versions.
+
+=head1 RETURN VALUES
+
+Returns 1 if it succeeds in printing, and 0 if it failed.
+
+=head1 COPYRIGHT
+
+Copyright 2024 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<https://www.openssl.org/source/license.html>.
+
+=cut
ASIdentifiers_new,
ASRange_free,
ASRange_new,
+ATTRIBUTES_SYNTAX_free,
+ATTRIBUTES_SYNTAX_it,
+ATTRIBUTES_SYNTAX_new,
AUTHORITY_INFO_ACCESS_free,
AUTHORITY_INFO_ACCESS_new,
AUTHORITY_KEYID_free,
d2i_ASN1_UTF8STRING,
d2i_ASN1_VISIBLESTRING,
d2i_ASRange,
+d2i_ATTRIBUTES_SYNTAX,
d2i_AUTHORITY_INFO_ACCESS,
d2i_AUTHORITY_KEYID,
d2i_BASIC_CONSTRAINTS,
i2d_ASN1_VISIBLESTRING,
i2d_ASN1_bio_stream,
i2d_ASRange,
+i2d_ATTRIBUTES_SYNTAX,
i2d_AUTHORITY_INFO_ACCESS,
i2d_AUTHORITY_KEYID,
i2d_BASIC_CONSTRAINTS,
int type,
const unsigned char *bytes,
int len);
+
+int ossl_print_attribute_value(BIO *out,
+ int obj_nid,
+ const ASN1_TYPE *av,
+ int indent);
+
#endif /* OSSL_CRYPTO_X509_H */
int OSSL_GENERAL_NAMES_print(BIO *out, GENERAL_NAMES *gens, int indent);
+typedef STACK_OF(X509_ATTRIBUTE) ATTRIBUTES_SYNTAX;
+DECLARE_ASN1_FUNCTIONS(ATTRIBUTES_SYNTAX)
+
# ifdef __cplusplus
}
# endif
--- /dev/null
+-----BEGIN CERTIFICATE-----
+MIIBYzCCAU2gAwIBAgIEDCI4TjANBgkqhkiG9w0BAQEFADARMQ8wDQYDVQQDDAZI
+aSBtb20wIhgPMjAyMjEwMjkwMTIzNDNaGA8yMDIyMTAyOTAxMjM0M1owETEPMA0G
+A1UEAwwGSGkgbW9tMAowBQYDKgMEAwEAo4HaMIHXMIHUBgNVHUsEgcwwgckwgX8G
+A1UEAzF4DAtTdGV2ZSBCcnVsZQwPRHIuIFN0ZXZlIEJydWxlDCJEci4gU3RldmUg
+QnJ1bGUsIGZyb20gQnJ1bGVzIFJ1bGVzDDRUaGUgZ2l2ZW5OYW1lIGF0dHJpYnV0
+ZSBiZWxvdyBpcyBpbnRlbnRpb25hbGx5IGVtcHR5MAwGA1UEajEFBgNVBAMwGgYD
+VQQgMRMwETEPMA0GA1UEAwwGSGkgbW9tMAcGA1UEKjEAMBIGA1UEBzELDAlGdW5r
+eXRvd24wDQYJKoZIhvcNAQEBBQADAQA=
+-----END CERTIFICATE-----
--- /dev/null
+-----BEGIN CERTIFICATE-----
+MIIBYzCCAU2gAwIBAgIEDCI4TjANBgkqhkiG9w0BAQEFADARMQ8wDQYDVQQDDAZI
+aSBtb20wIhgPMjAyMjEwMjkwMTI0NDlaGA8yMDIyMTAyOTAxMjQ0OVowETEPMA0G
+A1UEAwwGSGkgbW9tMAowBQYDKgMEAwEAo4HaMIHXMIHUBgNVHQkEgcwwgckwgX8G
+A1UEAzF4DAtTdGV2ZSBCcnVsZQwPRHIuIFN0ZXZlIEJydWxlDCJEci4gU3RldmUg
+QnJ1bGUsIGZyb20gQnJ1bGVzIFJ1bGVzDDRUaGUgZ2l2ZW5OYW1lIGF0dHJpYnV0
+ZSBiZWxvdyBpcyBpbnRlbnRpb25hbGx5IGVtcHR5MAwGA1UEajEFBgNVBAMwGgYD
+VQQgMRMwETEPMA0GA1UEAwwGSGkgbW9tMAcGA1UEKjEAMBIGA1UEBzELDAlGdW5r
+eXRvd24wDQYJKoZIhvcNAQEBBQADAQA=
+-----END CERTIFICATE-----
setup("test_x509");
-plan tests => 66;
+plan tests => 82;
# Prevent MSys2 filename munging for arguments that look like file paths but
# aren't
cert_contains($dnc_cert,
"DirName:CN = Wildboar",
1, 'X.509 Delegated Name Constraint');
+my $sda_cert = srctop_file(@certs, "ext-subjectDirectoryAttributes.pem");
+cert_contains($sda_cert,
+ "Steve Brule",
+ 1, 'X.509 Subject Directory Attributes');
+cert_contains($sda_cert,
+ "CN=Hi mom",
+ 1, 'X.509 Subject Directory Attributes');
+cert_contains($sda_cert,
+ "<No Values>",
+ 1, 'X.509 Subject Directory Attributes');
+cert_contains($sda_cert,
+ "Funkytown",
+ 1, 'X.509 Subject Directory Attributes');
+cert_contains($sda_cert,
+ "commonName",
+ 1, 'X.509 Subject Directory Attributes');
+cert_contains($sda_cert,
+ "owner",
+ 1, 'X.509 Subject Directory Attributes');
+cert_contains($sda_cert,
+ "givenName",
+ 1, 'X.509 Subject Directory Attributes');
+cert_contains($sda_cert,
+ "localityName",
+ 1, 'X.509 Subject Directory Attributes');
+
+my $ass_info_cert = srctop_file(@certs, "ext-associatedInformation.pem");
+cert_contains($ass_info_cert,
+ "Steve Brule",
+ 1, 'X509v3 Associated Information');
+cert_contains($ass_info_cert,
+ "CN=Hi mom",
+ 1, 'X509v3 Associated Information');
+cert_contains($ass_info_cert,
+ "<No Values>",
+ 1, 'X509v3 Associated Information');
+cert_contains($ass_info_cert,
+ "Funkytown",
+ 1, 'X509v3 Associated Information');
+cert_contains($ass_info_cert,
+ "commonName",
+ 1, 'X509v3 Associated Information');
+cert_contains($ass_info_cert,
+ "owner",
+ 1, 'X509v3 Associated Information');
+cert_contains($sda_cert,
+ "givenName",
+ 1, 'X509v3 Associated Information');
+cert_contains($ass_info_cert,
+ "localityName",
+ 1, 'X509v3 Associated Information');
sub test_errors { # actually tests diagnostics of OSSL_STORE
my ($expected, $cert, @opts) = @_;
OSSL_TARGETING_INFORMATION_new ? 3_4_0 EXIST::FUNCTION:
OSSL_TARGETING_INFORMATION_it ? 3_4_0 EXIST::FUNCTION:
OSSL_GENERAL_NAMES_print ? 3_4_0 EXIST::FUNCTION:
+d2i_ATTRIBUTES_SYNTAX ? 3_4_0 EXIST::FUNCTION:
+i2d_ATTRIBUTES_SYNTAX ? 3_4_0 EXIST::FUNCTION:
+ATTRIBUTES_SYNTAX_free ? 3_4_0 EXIST::FUNCTION:
+ATTRIBUTES_SYNTAX_new ? 3_4_0 EXIST::FUNCTION:
+ATTRIBUTES_SYNTAX_it ? 3_4_0 EXIST::FUNCTION: