Commit 3f51c9f7ac for openssl.org

commit 3f51c9f7ac3eddccba407b82c94d3ef19a9aa16a
Author: Jakub Zelenka <jakub.zelenka@openssl.foundation>
Date:   Wed Jul 15 12:20:03 2026 +0200

    cms: fix AuthenticatedData authAttrs and unauthAttrs element type

    The CMS_AuthenticatedData ASN.1 template declared the authAttrs and
    unauthAttrs fields as X509_ALGOR, whereas per RFC 5652 section 9.1 they
    are [2] and [3] IMPLICIT SET OF Attribute and the CMS_AuthenticatedData
    structure already declares them as STACK_OF(X509_ATTRIBUTE). The implicit
    tags [2] and [3] were already correct, so only the element type was wrong.

    An Attribute (SEQUENCE { type, SET OF value }) is structurally accepted as
    an AlgorithmIdentifier (SEQUENCE { algorithm, ANY OPTIONAL }), so parsing
    did not fail; the attributes were merely misinterpreted, e.g. cms -cmsout
    -print rendered them as algorithm/parameter instead of decoding them as
    attributes. Use X509_ATTRIBUTE with the existing tags so the template
    matches the structure.

    Add a parse test using a BouncyCastle-generated AuthenticatedData message
    carrying an authenticated and an unauthenticated attribute, asserting both
    are decoded as SET OF Attribute.

    Assisted-by: Claude:claude-opus-4-8

    Reviewed-by: Tomas Mraz <tomas@openssl.foundation>
    Reviewed-by: Paul Dale <paul.dale@oracle.com>
    MergeDate: Wed Jul 29 10:02:08 2026
    (Merged from https://github.com/openssl/openssl/pull/31960)

diff --git a/crypto/cms/cms_asn1.c b/crypto/cms/cms_asn1.c
index 96b125e930..f34240ca8e 100644
--- a/crypto/cms/cms_asn1.c
+++ b/crypto/cms/cms_asn1.c
@@ -318,9 +318,9 @@ ASN1_NDEF_SEQUENCE(CMS_AuthenticatedData) = {
     ASN1_SIMPLE(CMS_AuthenticatedData, macAlgorithm, X509_ALGOR),
     ASN1_IMP(CMS_AuthenticatedData, digestAlgorithm, X509_ALGOR, 1),
     ASN1_SIMPLE(CMS_AuthenticatedData, encapContentInfo, CMS_EncapsulatedContentInfo),
-    ASN1_IMP_SET_OF_OPT(CMS_AuthenticatedData, authAttrs, X509_ALGOR, 2),
+    ASN1_IMP_SET_OF_OPT(CMS_AuthenticatedData, authAttrs, X509_ATTRIBUTE, 2),
     ASN1_SIMPLE(CMS_AuthenticatedData, mac, ASN1_OCTET_STRING),
-    ASN1_IMP_SET_OF_OPT(CMS_AuthenticatedData, unauthAttrs, X509_ALGOR, 3)
+    ASN1_IMP_SET_OF_OPT(CMS_AuthenticatedData, unauthAttrs, X509_ATTRIBUTE, 3)
 } static_ASN1_NDEF_SEQUENCE_END(CMS_AuthenticatedData)

 ASN1_NDEF_SEQUENCE(CMS_CompressedData)
diff --git a/test/recipes/80-test_cms.t b/test/recipes/80-test_cms.t
index 7516cf024e..bd7ed2cc1e 100644
--- a/test/recipes/80-test_cms.t
+++ b/test/recipes/80-test_cms.t
@@ -56,7 +56,7 @@ my ($no_des, $no_dh, $no_dsa, $no_ec, $no_ec2m, $no_rc2, $no_zlib)

 $no_rc2 = 1 if disabled("legacy");

-plan tests => 40;
+plan tests => 41;

 ok(run(test(["pkcs7_test"])), "test pkcs7");

@@ -1069,6 +1069,29 @@ subtest "CMS decrypt authEnvelopedData with authenticated attributes\n" => sub {
        "tampered authEnvelopedData leaks no plaintext to -out");
 };

+subtest "CMS parse authenticatedData authAttrs and unauthAttrs\n" => sub {
+    plan tests => 3;
+
+    # BouncyCastle authenticatedData (HMAC-SHA256, KEK) carrying both an
+    # authenticated and an unauthenticated attribute. Per RFC 5652 these are
+    # SET OF Attribute, so with the CMS_AuthenticatedData template fixed to use
+    # X509_ATTRIBUTE they are rendered as attributes (object:/set:) rather than
+    # as an X509_ALGOR (algorithm:/parameter:) they were misparsed into before.
+    my $exit = 0;
+    my $dump = join "\n",
+               run(app(["openssl", "cms", @defaultprov, "-cmsout", "-noout",
+                        "-print", "-inform", "PEM",
+                        "-in", catfile($datadir, "authenticated_attrs.pem")]),
+                   capture => 1,
+                   statusvar => $exit);
+
+    is($exit, 0, "parse authenticatedData with attributes");
+    ok($dump =~ /authAttrs:.*?object:.*?1\.3\.6\.1\.4\.1\.5949\.99\.1.*?UTF8STRING:auth-attr-value/s,
+       "authAttrs parsed as SET OF Attribute");
+    ok($dump =~ /unauthAttrs:.*?object:.*?1\.3\.6\.1\.4\.1\.5949\.99\.2.*?UTF8STRING:unauth-attr-value/s,
+       "unauthAttrs parsed as SET OF Attribute");
+};
+
 subtest "CAdES <=> CAdES consistency tests\n" => sub {
     plan tests => (scalar @smime_cms_cades_tests);

diff --git a/test/recipes/80-test_cms_data/authenticated_attrs.pem b/test/recipes/80-test_cms_data/authenticated_attrs.pem
new file mode 100644
index 0000000000..4f3d212698
--- /dev/null
+++ b/test/recipes/80-test_cms_data/authenticated_attrs.pem
@@ -0,0 +1,8 @@
+-----BEGIN CMS-----
+MIAGCyqGSIb3DQEJEAECoIAwgAIBADFDokECAQQwBQQDwP7gMAsGCWCGSAFlAwQB
+BQQoWM396pUOzWW6mFsNvr+XXTLufCrvzG3jiTOX+l3LpSXbXHhhpadw5DAMBggq
+hkiG9w0CCQUAoQsGCWCGSAFlAwQCATCABgkqhkiG9w0BBwGggCSABB5IZWxsbyBB
+dXRoZW50aWNhdGVkRGF0YSB3b3JsZAoAAAAAAACiIDAeBgkrBgEEAa49YwExEQwP
+YXV0aC1hdHRyLXZhbHVlBCB9kCl8ic3e5461oodeDSyR7heZxtdN7G/N+oqerDIj
+PqMiMCAGCSsGAQQBrj1jAjETDBF1bmF1dGgtYXR0ci12YWx1ZQAAAAAAAA==
+-----END CMS-----