Support subjectDirectoryAttributes and associatedInformation exts

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)
This commit is contained in:
Jonathan M. Wilbur 2024-06-18 09:08:40 +00:00 committed by Tomas Mraz
parent 8f250985ad
commit be5adfd6e3
14 changed files with 464 additions and 2 deletions

View File

@ -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 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

View File

@ -34,3 +34,5 @@ extern const X509V3_EXT_METHOD ossl_v3_indirect_issuer;
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;

View File

@ -62,6 +62,7 @@ static const X509V3_EXT_METHOD *standard_exts[] = {
&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,
@ -81,6 +82,7 @@ static const X509V3_EXT_METHOD *standard_exts[] = {
&ossl_v3_single_use,
&ossl_v3_group_ac,
&ossl_v3_holder_name_constraints,
&ossl_v3_associated_info,
};
/* Number of standard extensions */

90
crypto/x509/v3_sda.c Normal file
View File

@ -0,0 +1,90 @@
/*
* 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
};

View File

@ -13,6 +13,7 @@
#include <openssl/asn1t.h>
#include <openssl/x509.h>
#include "x509_local.h"
#include <crypto/x509.h>
/*-
* X509_ATTRIBUTE: this has the following form:
@ -56,3 +57,228 @@ X509_ATTRIBUTE *X509_ATTRIBUTE_create(int nid, int atrtype, void *value)
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);
}
}

View File

@ -0,0 +1,52 @@
=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

View File

@ -19,6 +19,9 @@ ASIdentifiers_free,
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,

View File

@ -38,6 +38,7 @@ d2i_ASN1_UTCTIME,
d2i_ASN1_UTF8STRING,
d2i_ASN1_VISIBLESTRING,
d2i_ASRange,
d2i_ATTRIBUTES_SYNTAX,
d2i_AUTHORITY_INFO_ACCESS,
d2i_AUTHORITY_KEYID,
d2i_BASIC_CONSTRAINTS,
@ -220,6 +221,7 @@ i2d_ASN1_UTF8STRING,
i2d_ASN1_VISIBLESTRING,
i2d_ASN1_bio_stream,
i2d_ASRange,
i2d_ATTRIBUTES_SYNTAX,
i2d_AUTHORITY_INFO_ACCESS,
i2d_AUTHORITY_KEYID,
i2d_BASIC_CONSTRAINTS,

View File

@ -388,4 +388,10 @@ STACK_OF(X509_ATTRIBUTE) *ossl_x509at_add1_attr_by_txt(STACK_OF(X509_ATTRIBUTE)
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 */

View File

@ -1021,6 +1021,9 @@ void PROFESSION_INFO_set0_registrationNumber(
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

View File

@ -0,0 +1,10 @@
-----BEGIN CERTIFICATE-----
MIIBYzCCAU2gAwIBAgIEDCI4TjANBgkqhkiG9w0BAQEFADARMQ8wDQYDVQQDDAZI
aSBtb20wIhgPMjAyMjEwMjkwMTIzNDNaGA8yMDIyMTAyOTAxMjM0M1owETEPMA0G
A1UEAwwGSGkgbW9tMAowBQYDKgMEAwEAo4HaMIHXMIHUBgNVHUsEgcwwgckwgX8G
A1UEAzF4DAtTdGV2ZSBCcnVsZQwPRHIuIFN0ZXZlIEJydWxlDCJEci4gU3RldmUg
QnJ1bGUsIGZyb20gQnJ1bGVzIFJ1bGVzDDRUaGUgZ2l2ZW5OYW1lIGF0dHJpYnV0
ZSBiZWxvdyBpcyBpbnRlbnRpb25hbGx5IGVtcHR5MAwGA1UEajEFBgNVBAMwGgYD
VQQgMRMwETEPMA0GA1UEAwwGSGkgbW9tMAcGA1UEKjEAMBIGA1UEBzELDAlGdW5r
eXRvd24wDQYJKoZIhvcNAQEBBQADAQA=
-----END CERTIFICATE-----

View File

@ -0,0 +1,10 @@
-----BEGIN CERTIFICATE-----
MIIBYzCCAU2gAwIBAgIEDCI4TjANBgkqhkiG9w0BAQEFADARMQ8wDQYDVQQDDAZI
aSBtb20wIhgPMjAyMjEwMjkwMTI0NDlaGA8yMDIyMTAyOTAxMjQ0OVowETEPMA0G
A1UEAwwGSGkgbW9tMAowBQYDKgMEAwEAo4HaMIHXMIHUBgNVHQkEgcwwgckwgX8G
A1UEAzF4DAtTdGV2ZSBCcnVsZQwPRHIuIFN0ZXZlIEJydWxlDCJEci4gU3RldmUg
QnJ1bGUsIGZyb20gQnJ1bGVzIFJ1bGVzDDRUaGUgZ2l2ZW5OYW1lIGF0dHJpYnV0
ZSBiZWxvdyBpcyBpbnRlbnRpb25hbGx5IGVtcHR5MAwGA1UEajEFBgNVBAMwGgYD
VQQgMRMwETEPMA0GA1UEAwwGSGkgbW9tMAcGA1UEKjEAMBIGA1UEBzELDAlGdW5r
eXRvd24wDQYJKoZIhvcNAQEBBQADAQA=
-----END CERTIFICATE-----

View File

@ -16,7 +16,7 @@ use OpenSSL::Test qw/:DEFAULT srctop_file/;
setup("test_x509");
plan tests => 66;
plan tests => 82;
# Prevent MSys2 filename munging for arguments that look like file paths but
# aren't
@ -193,6 +193,57 @@ cert_contains($dnc_cert,
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) = @_;

View File

@ -5683,3 +5683,8 @@ OSSL_TARGETING_INFORMATION_free ? 3_4_0 EXIST::FUNCTION:
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: