Commit 723e67bb0d for openssl.org
commit 723e67bb0d66208730344e9d56db32b75a44cf06
Author: Mounir IDRASSI <mounir.idrassi@idrix.fr>
Date: Fri Aug 7 18:34:00 2026 +0900
Fix X509_ATTRIBUTE_set1_data() for BIT STRING attribute values
Commit 9044e5f42556 ("Convert internal use of ASN1_STRING_set and
ASN1_STRING_length") replaced ASN1_STRING_set() with the new
ASN1_STRING_set1_data() in X509_ATTRIBUTE_set1_data(). The new setter
intentionally rejects strings of type V_ASN1_BIT_STRING, so creating an
attribute value of type BIT STRING from raw bytes with an explicit
length now fails with ASN1_R_ILLEGAL_BITSTRING_FORMAT.
This broke PKCS8_add_keyusage() and therefore PKCS#12 creation with a
non-zero keytype (e.g. 'openssl pkcs12 -export -keyex' or '-keysig'),
as well as the public X509_ATTRIBUTE_create_by_*() and
X509at_add1_attr_by_*() APIs when used with BIT STRING values supplied
as bytes plus an explicit length.
Restore the previous behaviour by dispatching to ASN1_BIT_STRING_set1()
with zero unused bits when attrtype is V_ASN1_BIT_STRING, keeping
ASN1_STRING_set1_data() for the other types: the signature of this
explicit-length raw-data API provides no way to specify another
unused-bit count, so the data is taken as complete octets, which
preserves the previous encoding for supplied raw-byte values. Document
this in the X509_ATTRIBUTE manual page.
Fixes #32234
Assisted-by: Cline:Qwen3.8 Max
Reviewed-by: Richard Levitte <levitte@openssl.org>
Reviewed-by: Bob Beck <beck@openssl.org>
Merge-date: Wed Aug 19 15:44:48 2026
Merged-from: https://github.com/openssl/openssl/pull/32238
diff --git a/crypto/x509/x509_att.c b/crypto/x509/x509_att.c
index 0badec2b7d..a88f5d8279 100644
--- a/crypto/x509/x509_att.c
+++ b/crypto/x509/x509_att.c
@@ -364,8 +364,24 @@ int X509_ATTRIBUTE_set1_data(X509_ATTRIBUTE *attr, int attrtype,
}
atype = stmp->type;
} else if (len != -1) {
- if ((stmp = ASN1_STRING_type_new(attrtype)) == NULL
- || !ASN1_STRING_set1_data(stmp, data, len)) {
+ if ((stmp = ASN1_STRING_type_new(attrtype)) == NULL) {
+ ERR_raise(ERR_LIB_X509, ERR_R_ASN1_LIB);
+ goto err;
+ }
+ if (attrtype == V_ASN1_BIT_STRING) {
+ /*
+ * ASN1_STRING_set1_data() rejects bit strings, so use the
+ * dedicated bit string setter, with zero unused bits.
+ */
+ if (data == NULL && len > 0) {
+ ERR_raise(ERR_LIB_X509, ERR_R_PASSED_NULL_PARAMETER);
+ goto err;
+ }
+ if (!ASN1_BIT_STRING_set1(stmp, data, len, 0)) {
+ ERR_raise(ERR_LIB_X509, ERR_R_ASN1_LIB);
+ goto err;
+ }
+ } else if (!ASN1_STRING_set1_data(stmp, data, len)) {
ERR_raise(ERR_LIB_X509, ERR_R_ASN1_LIB);
goto err;
}
diff --git a/doc/man3/X509_ATTRIBUTE.pod b/doc/man3/X509_ATTRIBUTE.pod
index a7054395b7..868e8b690c 100644
--- a/doc/man3/X509_ATTRIBUTE.pod
+++ b/doc/man3/X509_ATTRIBUTE.pod
@@ -152,6 +152,9 @@ ASN1_STRING_set_by_NID(), and the passed in I<data> must be in the format
required for that object type or an error will occur.
If I<len> is not -1 then internally ASN1_STRING_type_new() is
used with the passed in I<attrtype>.
+If I<attrtype> is B<V_ASN1_BIT_STRING> and I<len> is not -1, I<data> is
+taken as complete octets of the bit string, so the resulting
+B<ASN1_BIT_STRING> is created with zero unused bits.
If I<attrtype> is 0 the call does nothing except return 1.
X509_ATTRIBUTE_create() creates a new B<X509_ATTRIBUTE> using the I<nid>