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>