xref: /freebsd/crypto/openssl/test/cmsapitest.c (revision dece5b05f577fc4c6b89af7ad48dfbdf7cc4945c)
1 /*
2  * Copyright 2018-2026 The OpenSSL Project Authors. All Rights Reserved.
3  *
4  * Licensed under the Apache License 2.0 (the "License").  You may not use
5  * this file except in compliance with the License.  You can obtain a copy
6  * in the file LICENSE in the source distribution or at
7  * https://www.openssl.org/source/license.html
8  */
9 
10 #include <string.h>
11 
12 #include <openssl/pem.h>
13 #include <openssl/cms.h>
14 #include <openssl/bio.h>
15 #include <openssl/x509.h>
16 #include "../crypto/cms/cms_local.h" /* for d.signedData and d.envelopedData */
17 
18 #include "testutil.h"
19 
20 static X509 *cert = NULL;
21 static EVP_PKEY *privkey = NULL;
22 static char *derin = NULL;
23 static char *too_long_iv_cms_in = NULL;
24 static char *pwri_kek_oob_der_in = NULL;
25 static char *pwri_kek_no_iv_in = NULL;
26 static char *ec_recip_in = NULL;
27 
28 /*
29  * This is our bad cms data, it contains an AuthEnvelopedData field
30  * with a CIPHER OID set to AES-256-OFB
31  */
32 static const unsigned char bad_cms_der[452] = {
33     0x30, 0x82, 0x01, 0xc0, 0x06, 0x0b, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
34     0x01, 0x09, 0x10, 0x01, 0x17, 0xa0, 0x82, 0x01, 0xaf, 0x30, 0x82, 0x01,
35     0xab, 0x02, 0x01, 0x00, 0x31, 0x82, 0x01, 0x44, 0x30, 0x82, 0x01, 0x40,
36     0x02, 0x01, 0x00, 0x30, 0x28, 0x30, 0x10, 0x31, 0x0e, 0x30, 0x0c, 0x06,
37     0x03, 0x55, 0x04, 0x03, 0x0c, 0x05, 0x52, 0x65, 0x63, 0x69, 0x70, 0x02,
38     0x14, 0x1a, 0x5c, 0x04, 0x9b, 0x3a, 0x64, 0xff, 0xd4, 0x63, 0xde, 0x4f,
39     0x90, 0xe5, 0x76, 0xe2, 0x18, 0xe8, 0x5c, 0x9e, 0xd7, 0x30, 0x0d, 0x06,
40     0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00,
41     0x04, 0x82, 0x01, 0x00, 0x18, 0xcf, 0x9f, 0x44, 0x95, 0x79, 0xe9, 0x96,
42     0x7d, 0x0f, 0xd1, 0xb4, 0xc2, 0x38, 0xb0, 0xc9, 0x76, 0xd5, 0xba, 0x08,
43     0x5c, 0xbf, 0xc3, 0x30, 0xea, 0x3a, 0x68, 0xa4, 0xba, 0x99, 0x4c, 0x70,
44     0x97, 0xb8, 0xa9, 0xce, 0x71, 0x4c, 0x54, 0xa3, 0xfd, 0x81, 0x9e, 0x15,
45     0x63, 0xb7, 0x23, 0x46, 0x17, 0x69, 0xaf, 0x8f, 0xbd, 0xa3, 0x54, 0x23,
46     0xf3, 0xf5, 0x35, 0xa8, 0xd4, 0x9c, 0xec, 0xe1, 0x17, 0x2c, 0x6d, 0x0b,
47     0xad, 0xc0, 0xe9, 0x1d, 0xd1, 0x8d, 0x59, 0xd5, 0x29, 0xc6, 0x40, 0xc4,
48     0xcd, 0x4e, 0x87, 0x70, 0x19, 0x5d, 0x88, 0x50, 0xbd, 0x4a, 0x13, 0xb3,
49     0xef, 0x0c, 0x6d, 0x6a, 0xc5, 0x51, 0xbb, 0x5c, 0x39, 0x17, 0xda, 0xb1,
50     0x71, 0x17, 0x88, 0xfb, 0x6a, 0xef, 0x7f, 0x85, 0xa7, 0x04, 0x71, 0xc7,
51     0x83, 0x91, 0xb3, 0x30, 0x1b, 0x3d, 0x18, 0x7f, 0x63, 0xbf, 0x42, 0x7c,
52     0xae, 0x6f, 0xae, 0xa1, 0x17, 0x84, 0xfd, 0x67, 0x2a, 0x4f, 0x4c, 0xe9,
53     0x05, 0x26, 0x2c, 0xd5, 0xab, 0x0c, 0xcf, 0xdc, 0x3f, 0x24, 0xcf, 0x71,
54     0x26, 0x7a, 0x1f, 0xf7, 0xc9, 0x92, 0x5e, 0xb6, 0x3d, 0x7f, 0xc3, 0x08,
55     0xd3, 0xad, 0xc0, 0xc8, 0x4f, 0x42, 0x0c, 0xf3, 0xac, 0x23, 0x11, 0xdf,
56     0x75, 0x84, 0x69, 0x8c, 0xa6, 0x59, 0x43, 0xfb, 0xf7, 0x6b, 0x62, 0xf0,
57     0xf7, 0x35, 0x07, 0xc4, 0xf8, 0xd5, 0x12, 0x4a, 0x16, 0x62, 0xbc, 0x04,
58     0xaa, 0x9a, 0x2e, 0xb2, 0x1a, 0xfa, 0x4c, 0x82, 0xce, 0x9e, 0xa8, 0x6d,
59     0xc1, 0x29, 0x59, 0xe0, 0x33, 0xb5, 0xa6, 0x47, 0x09, 0x2e, 0xbf, 0x60,
60     0xa6, 0xb3, 0x21, 0xa0, 0x15, 0xac, 0x92, 0x29, 0xb5, 0xe6, 0xe0, 0xd4,
61     0x8b, 0xd8, 0x21, 0xe2, 0x17, 0x98, 0xd1, 0x11, 0x5d, 0xc5, 0xae, 0x24,
62     0xe8, 0x92, 0xdb, 0x96, 0xa3, 0x5b, 0x58, 0xa7, 0x30, 0x4c, 0x06, 0x09,
63     0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x01, 0x30, 0x1d, 0x06,
64     0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x01, 0x2b, 0x04, 0x10,
65     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
66     0x00, 0x00, 0x00, 0x00, 0x80, 0x20, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
67     0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
68     0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
69     0x41, 0x41, 0x04, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
70     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
71 };
72 
73 /*
74  * This array represents a der encoded contentinfo structure addressed to
75  * servercert.pem, with the tag value of the aes-256-gcm cipher used to encrypt
76  * the contents of the mssages down to 1 byte.  Decoding it should fail
77  */
78 static const unsigned char one_byte_mac_cms_der[423] = {
79     0x30, 0x82, 0x01, 0xa3, 0x06, 0x0b, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
80     0x01, 0x09, 0x10, 0x01, 0x17, 0xa0, 0x82, 0x01, 0x92, 0x30, 0x82, 0x01,
81     0x8e, 0x02, 0x01, 0x00, 0x31, 0x82, 0x01, 0x33, 0x30, 0x82, 0x01, 0x2f,
82     0x02, 0x01, 0x00, 0x30, 0x17, 0x30, 0x12, 0x31, 0x10, 0x30, 0x0e, 0x06,
83     0x03, 0x55, 0x04, 0x03, 0x0c, 0x07, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43,
84     0x41, 0x02, 0x01, 0x02, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
85     0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x04, 0x82, 0x01, 0x00, 0x10,
86     0x3a, 0x8c, 0xee, 0x4e, 0xe2, 0x1f, 0xfe, 0xcc, 0x28, 0x39, 0x9e, 0x46,
87     0xbe, 0xa7, 0xd5, 0x02, 0x2a, 0x53, 0x06, 0x5f, 0x94, 0x6b, 0x69, 0x6d,
88     0x2d, 0xe8, 0x44, 0xa6, 0x43, 0x52, 0x82, 0x89, 0x2d, 0xf1, 0x9b, 0xb9,
89     0x9e, 0xa4, 0x8d, 0x77, 0xf1, 0xd2, 0x8e, 0x86, 0x79, 0x06, 0x3e, 0x90,
90     0xf0, 0xca, 0x9e, 0xb5, 0x35, 0xd5, 0x89, 0xf0, 0x7c, 0x06, 0xa0, 0x91,
91     0xbf, 0xf4, 0x61, 0xaa, 0x5c, 0x99, 0xa3, 0x64, 0x15, 0xfd, 0xf9, 0x90,
92     0xf0, 0xf3, 0x25, 0x5b, 0x48, 0xa1, 0xfb, 0x7a, 0xce, 0x63, 0xdc, 0xa9,
93     0xfe, 0x7c, 0xbe, 0x9c, 0xaa, 0xd3, 0x42, 0x0e, 0x4a, 0xc3, 0x4b, 0x4e,
94     0x76, 0x6d, 0x52, 0x54, 0x85, 0x4e, 0xab, 0x50, 0x2c, 0x5f, 0xc2, 0x8b,
95     0x9f, 0x1f, 0x0f, 0x8a, 0x7c, 0xb3, 0x0a, 0xde, 0x50, 0x9b, 0xef, 0x89,
96     0xf2, 0xea, 0x07, 0xca, 0x11, 0x76, 0x29, 0xaf, 0xe4, 0x59, 0x28, 0x19,
97     0x48, 0x96, 0x67, 0xdd, 0xdd, 0x01, 0xf0, 0x14, 0xbe, 0x3d, 0xa5, 0xa3,
98     0x83, 0x21, 0x39, 0x29, 0xb7, 0x8f, 0xb7, 0xf4, 0x85, 0x05, 0xee, 0xca,
99     0xbb, 0xbd, 0xc0, 0xaf, 0x0d, 0xf1, 0xef, 0x5f, 0x06, 0x05, 0xeb, 0x0e,
100     0x55, 0xf0, 0x7e, 0x13, 0x1a, 0x2a, 0x37, 0xd4, 0xba, 0x26, 0xc8, 0x2e,
101     0x6b, 0xc3, 0xe1, 0xcf, 0x28, 0xab, 0x0d, 0xab, 0xdd, 0xa7, 0xf4, 0xd3,
102     0x59, 0xcd, 0xc7, 0x2d, 0xa1, 0x56, 0x5f, 0x47, 0x77, 0x27, 0x17, 0x71,
103     0xae, 0x75, 0xc8, 0x71, 0x58, 0xf9, 0xab, 0x67, 0xda, 0x23, 0x62, 0xa0,
104     0x6d, 0xe5, 0x2d, 0x06, 0xb8, 0xc0, 0xac, 0xaa, 0x38, 0xa4, 0x0d, 0xb5,
105     0xb2, 0xce, 0xa7, 0x26, 0x0d, 0x3a, 0x88, 0x2f, 0x8d, 0x6c, 0xa0, 0xf6,
106     0x94, 0xf2, 0x2c, 0x37, 0x03, 0xaf, 0x67, 0x5c, 0xf3, 0x2c, 0xfb, 0xe8,
107     0x16, 0x9e, 0x55, 0x30, 0x4f, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
108     0x0d, 0x01, 0x07, 0x01, 0x30, 0x1e, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01,
109     0x65, 0x03, 0x04, 0x01, 0x2e, 0x30, 0x11, 0x04, 0x0c, 0x6b, 0x1d, 0xe5,
110     0xb2, 0x38, 0x0e, 0x17, 0x91, 0x9c, 0x9c, 0x40, 0x35, 0x02, 0x01, 0x10,
111     0x80, 0x22, 0xa0, 0x90, 0x75, 0x74, 0xdf, 0x2d, 0xba, 0x4f, 0xce, 0x4e,
112     0x7e, 0x52, 0xb0, 0x2e, 0x5f, 0xe0, 0x84, 0x01, 0xb1, 0x49, 0x0b, 0x69,
113     0xc7, 0x61, 0x63, 0x84, 0x3a, 0xfc, 0xaa, 0x86, 0xfc, 0x96, 0x4e, 0x6c,
114     0x04, 0x01, 0x92
115 };
116 
117 static int test_short_mac_on_auth_envelope_data(void)
118 {
119     int ret = 0;
120     const unsigned char *derptr = one_byte_mac_cms_der;
121     BIO *outmsgbio = BIO_new(BIO_s_mem());
122     CMS_ContentInfo *content = d2i_CMS_ContentInfo(NULL, &derptr, OSSL_NELEM(one_byte_mac_cms_der));
123 
124     if (!TEST_ptr(content))
125         goto end;
126 
127     /*
128      * We expect this to fail, as the tag value in the authEnvelopedData parameter is
129      * a single byte
130      */
131     if (!TEST_false(CMS_decrypt(content, privkey, cert, NULL, outmsgbio, CMS_TEXT)))
132         goto end;
133 
134     ret = 1;
135 end:
136     BIO_free(outmsgbio);
137     CMS_ContentInfo_free(content);
138     return ret;
139 }
140 
141 static int test_non_aead_on_auth_envelope_dec(void)
142 {
143     int ret = 0;
144     const unsigned char *derptr = bad_cms_der;
145     BIO *outmsgbio = BIO_new(BIO_s_mem());
146     CMS_ContentInfo *content = d2i_CMS_ContentInfo(NULL, &derptr, OSSL_NELEM(bad_cms_der));
147 
148     if (!TEST_ptr(content))
149         goto end;
150 
151     /*
152      * We expect this to fail
153      */
154     if (!TEST_false(CMS_decrypt(content, privkey, cert, NULL, outmsgbio,
155             CMS_TEXT)))
156         goto end;
157 
158     ret = 1;
159 end:
160     BIO_free(outmsgbio);
161     CMS_ContentInfo_free(content);
162     return ret;
163 }
164 
165 static int test_non_aead_on_auth_envelope_enc(void)
166 {
167     CMS_ContentInfo *content = NULL;
168     STACK_OF(X509) *certstack = sk_X509_new_null();
169     const EVP_CIPHER *cipher = EVP_aes_128_cbc();
170     const char *msg = "Hello world";
171     BIO *msgbio = BIO_new_mem_buf(msg, (int)strlen(msg));
172     BIO *outmsgbio = BIO_new(BIO_s_mem());
173     X509 *recip;
174     int i;
175     int ret = 0;
176 
177     if (!TEST_ptr(certstack) || !TEST_ptr(msgbio) || !TEST_ptr(outmsgbio))
178         goto end;
179 
180     if (!TEST_int_gt(sk_X509_push(certstack, cert), 0))
181         goto end;
182 
183     /*
184      * Emulate CMS_encrypt here, but use a non AEAD cipher
185      */
186     content = CMS_AuthEnvelopedData_create_ex(cipher, NULL, NULL);
187 
188     if (!TEST_ptr(content))
189         goto end;
190 
191     for (i = 0; i < sk_X509_num(certstack); i++) {
192         recip = sk_X509_value(certstack, i);
193         if (!TEST_ptr(CMS_add1_recipient_cert(content, recip, CMS_TEXT)))
194             goto end;
195     }
196 
197     /*
198      * We expect this to fail as we are using a non-AEAD cipher on
199      * AuthEnvelopedData
200      */
201     if (!TEST_int_eq(CMS_final(content, msgbio, NULL, CMS_TEXT), 0))
202         goto end;
203 
204     ret = 1;
205 end:
206     sk_X509_free(certstack);
207     BIO_free(msgbio);
208     BIO_free(outmsgbio);
209     CMS_ContentInfo_free(content);
210     return ret;
211 }
212 
213 static int test_encrypt_decrypt(const EVP_CIPHER *cipher)
214 {
215     int testresult = 0;
216     STACK_OF(X509) *certstack = sk_X509_new_null();
217     const char *msg = "Hello world";
218     BIO *msgbio = BIO_new_mem_buf(msg, strlen(msg));
219     BIO *outmsgbio = BIO_new(BIO_s_mem());
220     CMS_ContentInfo *content = NULL;
221     BIO *contentbio = NULL;
222     char buf[80];
223 
224     if (!TEST_ptr(certstack) || !TEST_ptr(msgbio) || !TEST_ptr(outmsgbio))
225         goto end;
226 
227     if (!TEST_int_gt(sk_X509_push(certstack, cert), 0))
228         goto end;
229 
230     content = CMS_encrypt(certstack, msgbio, cipher, CMS_TEXT);
231     if (!TEST_ptr(content))
232         goto end;
233 
234     if (!TEST_true(CMS_decrypt(content, privkey, cert, NULL, outmsgbio,
235             CMS_TEXT)))
236         goto end;
237 
238     if (!(EVP_CIPHER_get_flags(cipher) & EVP_CIPH_FLAG_AEAD_CIPHER)
239         && !TEST_ptr(contentbio = CMS_EnvelopedData_decrypt(content->d.envelopedData,
240                          NULL, privkey, cert, NULL,
241                          CMS_TEXT, NULL, NULL)))
242         goto end;
243 
244     /* Check we got the message we first started with */
245     if (!TEST_int_eq(BIO_gets(outmsgbio, buf, sizeof(buf)), strlen(msg))
246         || !TEST_int_eq(strcmp(buf, msg), 0))
247         goto end;
248 
249     testresult = 1;
250 end:
251     BIO_free(contentbio);
252     sk_X509_free(certstack);
253     BIO_free(msgbio);
254     BIO_free(outmsgbio);
255     CMS_ContentInfo_free(content);
256 
257     return testresult && TEST_int_eq(ERR_peek_error(), 0);
258 }
259 
260 static int test_encrypt_decrypt_aes_cbc(void)
261 {
262     return test_encrypt_decrypt(EVP_aes_128_cbc());
263 }
264 
265 static int test_encrypt_decrypt_aes_128_gcm(void)
266 {
267     return test_encrypt_decrypt(EVP_aes_128_gcm());
268 }
269 
270 static int test_encrypt_decrypt_aes_192_gcm(void)
271 {
272     return test_encrypt_decrypt(EVP_aes_192_gcm());
273 }
274 
275 static int test_encrypt_decrypt_aes_256_gcm(void)
276 {
277     return test_encrypt_decrypt(EVP_aes_256_gcm());
278 }
279 
280 static int test_CMS_add1_cert(void)
281 {
282     CMS_ContentInfo *cms = NULL;
283     int ret = 0;
284 
285     ret = TEST_ptr(cms = CMS_ContentInfo_new())
286         && TEST_ptr(CMS_add1_signer(cms, cert, privkey, NULL, 0))
287         && TEST_true(CMS_add1_cert(cms, cert)); /* add cert again */
288 
289     CMS_ContentInfo_free(cms);
290     return ret;
291 }
292 
293 static int test_d2i_CMS_bio_NULL(void)
294 {
295     BIO *bio, *content = NULL;
296     CMS_ContentInfo *cms = NULL;
297     unsigned int flags = CMS_NO_SIGNER_CERT_VERIFY;
298     int ret = 0;
299 
300     /*
301      * Test data generated using:
302      * openssl cms -sign -md sha256 -signer ./test/certs/rootCA.pem -inkey \
303      * ./test/certs/rootCA.key -nodetach -outform DER -in ./in.txt -out out.der \
304      * -nosmimecap
305      */
306     static const unsigned char cms_data[] = {
307         0x30, 0x82, 0x05, 0xc5, 0x06, 0x09, 0x2a, 0x86,
308         0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x02, 0xa0,
309         0x82, 0x05, 0xb6, 0x30, 0x82, 0x05, 0xb2, 0x02,
310         0x01, 0x01, 0x31, 0x0d, 0x30, 0x0b, 0x06, 0x09,
311         0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02,
312         0x01, 0x30, 0x1c, 0x06, 0x09, 0x2a, 0x86, 0x48,
313         0x86, 0xf7, 0x0d, 0x01, 0x07, 0x01, 0xa0, 0x0f,
314         0x04, 0x0d, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20,
315         0x57, 0x6f, 0x72, 0x6c, 0x64, 0x0d, 0x0a, 0xa0,
316         0x82, 0x03, 0x83, 0x30, 0x82, 0x03, 0x7f, 0x30,
317         0x82, 0x02, 0x67, 0xa0, 0x03, 0x02, 0x01, 0x02,
318         0x02, 0x09, 0x00, 0x88, 0x43, 0x29, 0xcb, 0xc2,
319         0xeb, 0x15, 0x9a, 0x30, 0x0d, 0x06, 0x09, 0x2a,
320         0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b,
321         0x05, 0x00, 0x30, 0x56, 0x31, 0x0b, 0x30, 0x09,
322         0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x41,
323         0x55, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55,
324         0x04, 0x08, 0x0c, 0x0a, 0x53, 0x6f, 0x6d, 0x65,
325         0x2d, 0x53, 0x74, 0x61, 0x74, 0x65, 0x31, 0x21,
326         0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c,
327         0x18, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65,
328         0x74, 0x20, 0x57, 0x69, 0x64, 0x67, 0x69, 0x74,
329         0x73, 0x20, 0x50, 0x74, 0x79, 0x20, 0x4c, 0x74,
330         0x64, 0x31, 0x0f, 0x30, 0x0d, 0x06, 0x03, 0x55,
331         0x04, 0x03, 0x0c, 0x06, 0x72, 0x6f, 0x6f, 0x74,
332         0x43, 0x41, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x35,
333         0x30, 0x37, 0x30, 0x32, 0x31, 0x33, 0x31, 0x35,
334         0x31, 0x31, 0x5a, 0x17, 0x0d, 0x33, 0x35, 0x30,
335         0x37, 0x30, 0x32, 0x31, 0x33, 0x31, 0x35, 0x31,
336         0x31, 0x5a, 0x30, 0x56, 0x31, 0x0b, 0x30, 0x09,
337         0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x41,
338         0x55, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55,
339         0x04, 0x08, 0x0c, 0x0a, 0x53, 0x6f, 0x6d, 0x65,
340         0x2d, 0x53, 0x74, 0x61, 0x74, 0x65, 0x31, 0x21,
341         0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c,
342         0x18, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65,
343         0x74, 0x20, 0x57, 0x69, 0x64, 0x67, 0x69, 0x74,
344         0x73, 0x20, 0x50, 0x74, 0x79, 0x20, 0x4c, 0x74,
345         0x64, 0x31, 0x0f, 0x30, 0x0d, 0x06, 0x03, 0x55,
346         0x04, 0x03, 0x0c, 0x06, 0x72, 0x6f, 0x6f, 0x74,
347         0x43, 0x41, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d,
348         0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
349         0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01,
350         0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82,
351         0x01, 0x01, 0x00, 0xc0, 0xf1, 0x6b, 0x77, 0x88,
352         0xac, 0x35, 0xdf, 0xfb, 0x73, 0x53, 0x2f, 0x92,
353         0x80, 0x2f, 0x74, 0x16, 0x32, 0x4d, 0xf5, 0x10,
354         0x20, 0x6f, 0x6c, 0x3a, 0x8e, 0xd1, 0xdc, 0x6b,
355         0xe1, 0x2e, 0x3e, 0xc3, 0x04, 0x0f, 0xbf, 0x9b,
356         0xc4, 0xc9, 0x12, 0xd1, 0xe4, 0x0b, 0x45, 0x97,
357         0xe5, 0x06, 0xcd, 0x66, 0x3a, 0xe1, 0xe0, 0xe2,
358         0x2b, 0xdf, 0xa2, 0xc4, 0xec, 0x7b, 0xd3, 0x3d,
359         0x3c, 0x8a, 0xff, 0x5e, 0x74, 0xa0, 0xab, 0xa7,
360         0x03, 0x6a, 0x16, 0x5b, 0x5e, 0x92, 0xc4, 0x7e,
361         0x5b, 0x79, 0x8a, 0x69, 0xd4, 0xbc, 0x83, 0x5e,
362         0xae, 0x42, 0x92, 0x74, 0xa5, 0x2b, 0xe7, 0x00,
363         0xc1, 0xa9, 0xdc, 0xd5, 0xb1, 0x53, 0x07, 0x0f,
364         0x73, 0xf7, 0x8e, 0xad, 0x14, 0x3e, 0x25, 0x9e,
365         0xe5, 0x1e, 0xe6, 0xcc, 0x91, 0xcd, 0x95, 0x0c,
366         0x80, 0x44, 0x20, 0xc3, 0xfd, 0x17, 0xcf, 0x91,
367         0x3d, 0x63, 0x10, 0x1c, 0x14, 0x5b, 0xfb, 0xc3,
368         0xa8, 0xc1, 0x88, 0xb2, 0x77, 0xff, 0x9c, 0xdb,
369         0xfc, 0x6a, 0x44, 0x44, 0x44, 0xf7, 0x85, 0xec,
370         0x08, 0x2c, 0xd4, 0xdf, 0x81, 0xa3, 0x79, 0xc9,
371         0xfe, 0x1e, 0x9b, 0x93, 0x16, 0x53, 0xb7, 0x97,
372         0xab, 0xbe, 0x4f, 0x1a, 0xa5, 0xe2, 0xfa, 0x46,
373         0x05, 0xe4, 0x0d, 0x9c, 0x2a, 0xa4, 0xcc, 0xb9,
374         0x1e, 0x21, 0xa0, 0x6c, 0xc4, 0xab, 0x59, 0xb0,
375         0x40, 0x39, 0xbb, 0xf9, 0x88, 0xad, 0xfd, 0xdf,
376         0x8d, 0xb4, 0x0b, 0xaf, 0x7e, 0x41, 0xe0, 0x21,
377         0x3c, 0xc8, 0x33, 0x45, 0x49, 0x84, 0x2f, 0x93,
378         0x06, 0xee, 0xfd, 0x4f, 0xed, 0x4f, 0xf3, 0xbc,
379         0x9b, 0xde, 0xfc, 0x25, 0x5e, 0x55, 0xd5, 0x75,
380         0xd4, 0xc5, 0x7b, 0x3a, 0x40, 0x35, 0x06, 0x9f,
381         0xc4, 0x84, 0xb4, 0x6c, 0x93, 0x0c, 0xaf, 0x37,
382         0x5a, 0xaf, 0xb6, 0x41, 0x4d, 0x26, 0x23, 0x1c,
383         0xb8, 0x02, 0xb3, 0x02, 0x03, 0x01, 0x00, 0x01,
384         0xa3, 0x50, 0x30, 0x4e, 0x30, 0x0c, 0x06, 0x03,
385         0x55, 0x1d, 0x13, 0x04, 0x05, 0x30, 0x03, 0x01,
386         0x01, 0xff, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d,
387         0x0e, 0x04, 0x16, 0x04, 0x14, 0x85, 0x56, 0x89,
388         0x35, 0xe2, 0x9f, 0x00, 0x1a, 0xe1, 0x86, 0x03,
389         0x0b, 0x4b, 0xaf, 0x76, 0x12, 0x6b, 0x33, 0x6d,
390         0xfd, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23,
391         0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x85, 0x56,
392         0x89, 0x35, 0xe2, 0x9f, 0x00, 0x1a, 0xe1, 0x86,
393         0x03, 0x0b, 0x4b, 0xaf, 0x76, 0x12, 0x6b, 0x33,
394         0x6d, 0xfd, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86,
395         0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05,
396         0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x32, 0x0a,
397         0xbf, 0x2a, 0x0a, 0xe2, 0xbb, 0x4f, 0x43, 0xce,
398         0x88, 0xda, 0x5a, 0x39, 0x10, 0x37, 0x80, 0xbb,
399         0x37, 0x2d, 0x5e, 0x2d, 0x88, 0xdd, 0x26, 0x69,
400         0x9c, 0xe7, 0xb4, 0x98, 0x20, 0xb1, 0x25, 0xe6,
401         0x61, 0x59, 0x6d, 0x12, 0xec, 0x9b, 0x87, 0xbe,
402         0x57, 0xe1, 0x12, 0x05, 0xc5, 0x04, 0xf1, 0x17,
403         0xce, 0x14, 0xb8, 0x1c, 0x92, 0xd4, 0x95, 0x95,
404         0x2c, 0x5b, 0x28, 0x89, 0xfb, 0x72, 0x9c, 0x20,
405         0xd3, 0x32, 0x81, 0xa8, 0x85, 0xec, 0xc8, 0x08,
406         0x7b, 0xa8, 0x59, 0x5b, 0x3a, 0x6c, 0x31, 0xab,
407         0x52, 0xe2, 0x66, 0xcd, 0x14, 0x49, 0x5c, 0xf3,
408         0xd3, 0x3e, 0x62, 0xbc, 0x91, 0x16, 0xb4, 0x1c,
409         0xf5, 0xdd, 0x54, 0xaa, 0x3c, 0x61, 0x97, 0x79,
410         0xac, 0xe4, 0xc8, 0x43, 0x35, 0xc3, 0x0f, 0xfc,
411         0xf3, 0x70, 0x1d, 0xaf, 0xf0, 0x9c, 0x8a, 0x2a,
412         0x92, 0x93, 0x48, 0xaa, 0xd0, 0xe8, 0x47, 0xbe,
413         0x35, 0xc1, 0xc6, 0x7b, 0x6d, 0xda, 0xfa, 0x5d,
414         0x57, 0x45, 0xf3, 0xea, 0x41, 0x8f, 0x36, 0xc1,
415         0x3c, 0xf4, 0x52, 0x7f, 0x6e, 0x31, 0xdd, 0xba,
416         0x9a, 0xbc, 0x70, 0x56, 0x71, 0x38, 0xdc, 0x49,
417         0x57, 0x0c, 0xfd, 0x91, 0x17, 0xc5, 0xea, 0x87,
418         0xe5, 0x23, 0x74, 0x19, 0xb2, 0xb6, 0x99, 0x0c,
419         0x6b, 0xa2, 0x05, 0xf8, 0x51, 0x68, 0xed, 0x97,
420         0xe0, 0xdf, 0x62, 0xf9, 0x7e, 0x7a, 0x3a, 0x44,
421         0x71, 0x83, 0x57, 0x28, 0x49, 0x88, 0x69, 0xb5,
422         0x14, 0x1e, 0xda, 0x46, 0xe3, 0x6e, 0x78, 0xe1,
423         0xcb, 0x8f, 0xb5, 0x98, 0xb3, 0x2d, 0x6e, 0x5b,
424         0xb7, 0xf6, 0x93, 0x24, 0x14, 0x1f, 0xa4, 0xf6,
425         0x69, 0xbd, 0xff, 0x4c, 0x52, 0x50, 0x02, 0xc5,
426         0x43, 0x8d, 0x14, 0xe2, 0xd0, 0x75, 0x9f, 0x12,
427         0x5e, 0x94, 0x89, 0xd1, 0xef, 0x77, 0x89, 0x7d,
428         0x89, 0xd9, 0x9e, 0x76, 0x99, 0x24, 0x31, 0x82,
429         0x01, 0xf7, 0x30, 0x82, 0x01, 0xf3, 0x02, 0x01,
430         0x01, 0x30, 0x63, 0x30, 0x56, 0x31, 0x0b, 0x30,
431         0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
432         0x41, 0x55, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03,
433         0x55, 0x04, 0x08, 0x0c, 0x0a, 0x53, 0x6f, 0x6d,
434         0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, 0x65, 0x31,
435         0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0a,
436         0x0c, 0x18, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e,
437         0x65, 0x74, 0x20, 0x57, 0x69, 0x64, 0x67, 0x69,
438         0x74, 0x73, 0x20, 0x50, 0x74, 0x79, 0x20, 0x4c,
439         0x74, 0x64, 0x31, 0x0f, 0x30, 0x0d, 0x06, 0x03,
440         0x55, 0x04, 0x03, 0x0c, 0x06, 0x72, 0x6f, 0x6f,
441         0x74, 0x43, 0x41, 0x02, 0x09, 0x00, 0x88, 0x43,
442         0x29, 0xcb, 0xc2, 0xeb, 0x15, 0x9a, 0x30, 0x0b,
443         0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03,
444         0x04, 0x02, 0x01, 0xa0, 0x69, 0x30, 0x18, 0x06,
445         0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01,
446         0x09, 0x03, 0x31, 0x0b, 0x06, 0x09, 0x2a, 0x86,
447         0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x01, 0x30,
448         0x1c, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
449         0x0d, 0x01, 0x09, 0x05, 0x31, 0x0f, 0x17, 0x0d,
450         0x32, 0x30, 0x31, 0x32, 0x31, 0x31, 0x30, 0x39,
451         0x30, 0x30, 0x31, 0x33, 0x5a, 0x30, 0x2f, 0x06,
452         0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01,
453         0x09, 0x04, 0x31, 0x22, 0x04, 0x20, 0xb0, 0x80,
454         0x22, 0xd3, 0x15, 0xcf, 0x1e, 0xb1, 0x2d, 0x26,
455         0x65, 0xbd, 0xed, 0x0e, 0x6a, 0xf4, 0x06, 0x53,
456         0xc0, 0xa0, 0xbe, 0x97, 0x52, 0x32, 0xfb, 0x49,
457         0xbc, 0xbd, 0x02, 0x1c, 0xfc, 0x36, 0x30, 0x0d,
458         0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
459         0x01, 0x01, 0x01, 0x05, 0x00, 0x04, 0x82, 0x01,
460         0x00, 0x37, 0x44, 0x39, 0x08, 0xb2, 0x19, 0x52,
461         0x35, 0x9c, 0xd0, 0x67, 0x87, 0xae, 0xb8, 0x1c,
462         0x80, 0xf4, 0x03, 0x29, 0x2e, 0xe3, 0x76, 0x4a,
463         0xb0, 0x98, 0x10, 0x00, 0x9a, 0x30, 0xdb, 0x05,
464         0x28, 0x53, 0x34, 0x31, 0x14, 0xbd, 0x87, 0xb9,
465         0x4d, 0x45, 0x07, 0x97, 0xa3, 0x57, 0x0b, 0x7e,
466         0xd1, 0x67, 0xfb, 0x4e, 0x0f, 0x5b, 0x90, 0xb2,
467         0x6f, 0xe6, 0xce, 0x49, 0xdd, 0x72, 0x46, 0x71,
468         0x26, 0xa1, 0x1b, 0x98, 0x23, 0x7d, 0x69, 0x73,
469         0x84, 0xdc, 0xf9, 0xd2, 0x1c, 0x6d, 0xf6, 0xf5,
470         0x17, 0x49, 0x6e, 0x9d, 0x4d, 0xf1, 0xe2, 0x43,
471         0x29, 0x53, 0x55, 0xa5, 0x22, 0x1e, 0x89, 0x2c,
472         0xaf, 0xf2, 0x43, 0x47, 0xd5, 0xfa, 0xad, 0xe7,
473         0x89, 0x60, 0xbf, 0x96, 0x35, 0x6f, 0xc2, 0x99,
474         0xb7, 0x55, 0xc5, 0xe3, 0x04, 0x25, 0x1b, 0xf6,
475         0x7e, 0xf2, 0x2b, 0x14, 0xa9, 0x57, 0x96, 0xbe,
476         0xbd, 0x6e, 0x95, 0x44, 0x94, 0xbd, 0xaf, 0x9a,
477         0x6d, 0x77, 0x55, 0x5e, 0x6c, 0xf6, 0x32, 0x37,
478         0xec, 0xef, 0xe5, 0x81, 0xb0, 0xe3, 0x35, 0xc7,
479         0x86, 0xea, 0x47, 0x59, 0x38, 0xb6, 0x16, 0xfb,
480         0x1d, 0x10, 0x55, 0x48, 0xb1, 0x44, 0x33, 0xde,
481         0xf6, 0x29, 0xbe, 0xbf, 0xbc, 0x71, 0x3e, 0x49,
482         0xba, 0xe7, 0x9f, 0x4d, 0x6c, 0xfb, 0xec, 0xd2,
483         0xe0, 0x12, 0xa9, 0x7c, 0xc9, 0x9a, 0x7b, 0x85,
484         0x83, 0xb8, 0xca, 0xdd, 0xf6, 0xb7, 0x15, 0x75,
485         0x7b, 0x4a, 0x69, 0xcf, 0x0a, 0xc7, 0x80, 0x01,
486         0xe7, 0x94, 0x16, 0x7f, 0x8d, 0x3c, 0xfa, 0x1f,
487         0x05, 0x71, 0x76, 0x15, 0xb0, 0xf6, 0x61, 0x30,
488         0x58, 0x16, 0xbe, 0x1b, 0xd1, 0x93, 0xc4, 0x1a,
489         0x91, 0x0c, 0x48, 0xe2, 0x1c, 0x8e, 0xa5, 0xc5,
490         0xa7, 0x81, 0x44, 0x48, 0x3b, 0x10, 0xc2, 0x74,
491         0x07, 0xdf, 0xa8, 0xae, 0x57, 0xee, 0x7f, 0xe3,
492         0x6a
493     };
494 
495     ret = TEST_ptr(bio = BIO_new_mem_buf(cms_data, sizeof(cms_data)))
496         && TEST_ptr(cms = d2i_CMS_bio(bio, NULL))
497         && TEST_true(CMS_verify(cms, NULL, NULL, NULL, NULL, flags))
498         && TEST_ptr(content = CMS_SignedData_verify(cms->d.signedData, NULL, NULL, NULL,
499                         NULL, NULL, flags, NULL, NULL));
500     BIO_free(content);
501     CMS_ContentInfo_free(cms);
502     BIO_free(bio);
503     return ret && TEST_int_eq(ERR_peek_error(), 0);
504 }
505 
506 static unsigned char *read_all(BIO *bio, long *p_len)
507 {
508     const int step = 256;
509     unsigned char *buf = NULL;
510     unsigned char *tmp = NULL;
511     int ret;
512 
513     *p_len = 0;
514     for (;;) {
515         tmp = OPENSSL_realloc(buf, *p_len + step);
516         if (tmp == NULL)
517             break;
518         buf = tmp;
519         ret = BIO_read(bio, buf + *p_len, step);
520         if (ret < 0)
521             break;
522 
523         if (LONG_MAX - ret < *p_len)
524             break;
525 
526         *p_len += ret;
527 
528         if (ret < step)
529             return buf;
530     }
531 
532     /* Error */
533     OPENSSL_free(buf);
534     *p_len = 0;
535     return NULL;
536 }
537 
538 static int test_d2i_CMS_decode(const int idx)
539 {
540     BIO *bio = NULL;
541     CMS_ContentInfo *cms = NULL;
542     unsigned char *buf = NULL;
543     const unsigned char *tmp = NULL;
544     long buf_len = 0;
545     int ret = 0;
546 
547     if (!TEST_ptr(bio = BIO_new_file(derin, "r")))
548         goto end;
549 
550     switch (idx) {
551     case 0:
552         if (!TEST_ptr(cms = d2i_CMS_bio(bio, NULL)))
553             goto end;
554         break;
555     case 1:
556         if (!TEST_ptr(buf = read_all(bio, &buf_len)))
557             goto end;
558         tmp = buf;
559         if (!TEST_ptr(cms = d2i_CMS_ContentInfo(NULL, &tmp, buf_len)))
560             goto end;
561         break;
562     }
563 
564     if (!TEST_int_eq(ERR_peek_error(), 0))
565         goto end;
566 
567     ret = 1;
568 end:
569     CMS_ContentInfo_free(cms);
570     BIO_free(bio);
571     OPENSSL_free(buf);
572 
573     return ret;
574 }
575 
576 static int test_CMS_set1_key_mem_leak(void)
577 {
578     CMS_ContentInfo *cms;
579     unsigned char key[32] = { 0 };
580     int ret = 0;
581 
582     if (!TEST_ptr(cms = CMS_ContentInfo_new()))
583         return 0;
584 
585     if (!TEST_true(CMS_EncryptedData_set1_key(cms, EVP_aes_256_cbc(),
586             key, 32)))
587         goto end;
588 
589     if (!TEST_true(CMS_EncryptedData_set1_key(cms, EVP_aes_128_cbc(),
590             key, 16)))
591         goto end;
592 
593     ret = 1;
594 end:
595     CMS_ContentInfo_free(cms);
596     return ret;
597 }
598 
599 static int test_encrypted_data(void)
600 {
601     const char *msg = "Hello world";
602     BIO *msgbio = BIO_new_mem_buf(msg, (int)strlen(msg));
603     uint8_t key[16] = { 0 };
604     size_t keylen = 16;
605     CMS_ContentInfo *cms;
606     BIO *decryptbio = BIO_new(BIO_s_mem());
607     char buf[80];
608     int ret = 0;
609 
610     cms = CMS_EncryptedData_encrypt(msgbio, EVP_aes_128_cbc(), key, keylen, SMIME_BINARY);
611     if (!TEST_ptr(cms))
612         goto end;
613 
614     if (!TEST_true(CMS_EncryptedData_decrypt(cms, key, keylen, NULL, decryptbio, SMIME_BINARY)))
615         goto end;
616 
617     /* Check we got the message we first started with */
618     if (!TEST_int_eq(BIO_gets(decryptbio, buf, sizeof(buf)), (int)strlen(msg))
619         || !TEST_int_eq(strcmp(buf, msg), 0))
620         goto end;
621 
622     ret = 1;
623 end:
624     CMS_ContentInfo_free(cms);
625     BIO_free(msgbio);
626     BIO_free(decryptbio);
627     return ret;
628 }
629 
630 static int test_encrypted_data_aead(void)
631 {
632     const char *msg = "Hello world";
633     BIO *msgbio = BIO_new_mem_buf(msg, (int)strlen(msg));
634     uint8_t key[16] = { 0 };
635     size_t keylen = 16;
636     CMS_ContentInfo *cms;
637     BIO *decryptbio = BIO_new(BIO_s_mem());
638     int ret = 0;
639 
640     cms = CMS_ContentInfo_new();
641     if (!TEST_ptr(cms))
642         goto end;
643 
644     /*
645      * AEAD algorithms are not supported by the CMS EncryptedData so setting
646      * the cipher to AES GCM 128 will result in a failure
647      */
648     if (!TEST_false(CMS_EncryptedData_set1_key(cms, EVP_aes_128_gcm(), key, keylen)))
649         goto end;
650 
651     CMS_ContentInfo_free(cms);
652     cms = NULL;
653 
654     /*
655      * AEAD algorithms are not supported by the CMS EncryptedData so setting
656      * the cipher to AES GCM 128 will result in a failure
657      */
658     cms = CMS_EncryptedData_encrypt(msgbio, EVP_aes_128_gcm(), key, keylen, SMIME_BINARY);
659     if (!TEST_ptr_null(cms))
660         goto end;
661 
662     ret = 1;
663 
664 end:
665     CMS_ContentInfo_free(cms);
666     BIO_free(msgbio);
667     BIO_free(decryptbio);
668     return ret;
669 }
670 
671 static int test_cms_aesgcm_iv_too_long(void)
672 {
673     int ret = 0;
674     BIO *cmsbio = NULL, *out = NULL;
675     CMS_ContentInfo *cms = NULL;
676     unsigned long err = 0;
677 
678     if (!TEST_ptr(cmsbio = BIO_new_file(too_long_iv_cms_in, "r")))
679         goto end;
680 
681     if (!TEST_ptr(cms = PEM_read_bio_CMS(cmsbio, NULL, NULL, NULL)))
682         goto end;
683 
684     /* Must fail cleanly (no crash) */
685     if (!TEST_false(CMS_decrypt(cms, privkey, cert, NULL, out, 0)))
686         goto end;
687     err = ERR_peek_last_error();
688     if (!TEST_ulong_ne(err, 0))
689         goto end;
690     if (!TEST_int_eq(ERR_GET_LIB(err), ERR_LIB_CMS))
691         goto end;
692     if (!TEST_int_eq(ERR_GET_REASON(err), CMS_R_CIPHER_PARAMETER_INITIALISATION_ERROR))
693         goto end;
694 
695     ret = 1;
696 end:
697     CMS_ContentInfo_free(cms);
698     BIO_free(cmsbio);
699     BIO_free(out);
700     return ret;
701 }
702 
703 /*
704  * CMS EnvelopedData with a single PasswordRecipientInfo using
705  * id-alg-PWRI-KEK and an AES-128-CFB key encryption cipher
706  * (1-byte effective block size).  The encryptedKey OCTET STRING is
707  * only two bytes long, so the wrapped key buffer is shorter than
708  * the seven octets read by the check-byte test in kek_unwrap_key().
709  * Prior to CVE-2026-9076 this triggered an out-of-bounds heap read;
710  * CMS_decrypt() must now fail cleanly.
711  */
712 static int test_pwri_kek_unwrap_short_encrypted_key(void)
713 {
714     BIO *in = NULL;
715     CMS_ContentInfo *cms = NULL;
716     unsigned long err = 0;
717     int ret = 0;
718 
719     if (!TEST_ptr(in = BIO_new_file(pwri_kek_oob_der_in, "rb"))
720         || !TEST_ptr(cms = d2i_CMS_bio(in, NULL)))
721         goto end;
722 
723     /*
724      * The unwrap is attempted eagerly inside CMS_decrypt_set1_password().
725      * It must fail cleanly (no OOB read) and report CMS_R_UNWRAP_FAILURE.
726      */
727     if (!TEST_false(CMS_decrypt_set1_password(cms,
728             (unsigned char *)"password", -1)))
729         goto end;
730 
731     err = ERR_peek_last_error();
732     if (!TEST_int_eq(ERR_GET_LIB(err), ERR_LIB_CMS)
733         || !TEST_int_eq(ERR_GET_REASON(err), CMS_R_UNWRAP_FAILURE))
734         goto end;
735 
736     ERR_clear_error();
737     ret = 1;
738 end:
739     CMS_ContentInfo_free(cms);
740     BIO_free(in);
741     return ret;
742 }
743 
744 static int test_pwri_kek_unwrap_no_iv_key(void)
745 {
746     BIO *in = NULL;
747     CMS_ContentInfo *cms = NULL;
748     unsigned long err = 0;
749     int ret = 0;
750 
751     if (!TEST_ptr(in = BIO_new_file(pwri_kek_no_iv_in, "rb"))
752         || !TEST_ptr(cms = d2i_CMS_bio(in, NULL)))
753         goto end;
754 
755     /*
756      * Due to the missing IV, the unwrap must fail with
757      * CMS_R_CIPHER_PARAMETER_INITIALISATION_ERROR.
758      */
759     if (!TEST_false(CMS_decrypt_set1_password(cms,
760             (unsigned char *)"password", -1)))
761         goto end;
762 
763     err = ERR_peek_last_error();
764     if (!TEST_int_eq(ERR_GET_LIB(err), ERR_LIB_CMS)
765         || !TEST_int_eq(ERR_GET_REASON(err),
766             CMS_R_CIPHER_PARAMETER_INITIALISATION_ERROR))
767         goto end;
768 
769     ERR_clear_error();
770     ret = 1;
771 end:
772     CMS_ContentInfo_free(cms);
773     BIO_free(in);
774     return ret;
775 }
776 
777 #ifndef OPENSSL_NO_EC
778 
779 /*
780  * Regression test for CVE-2026-63072: an 8-byte out-of-bounds heap write
781  * reachable through CMS_decrypt() when a KeyAgreeRecipientInfo names an
782  * id-aesNNN-wrap-pad key-wrap OID. CMS sizes the unwrap output buffer from
783  * the cipher's length query (inlen - 8), but AES-WRAP-PAD unwrap cleanses
784  * inlen bytes of it on every RFC 5649 integrity-failure path.
785  *
786  * We build a valid ECDH KARI message (which uses non-padded id-aes256-wrap),
787  * flip the single OID byte an attacker would flip on the wire to turn it into
788  * id-aes256-wrap-pad (key length unchanged), and decrypt with the matching
789  * private key. The unwrap must fail its integrity check without writing past
790  * the CMS-allocated buffer; CMS_decrypt() must fail cleanly.  Under a
791  * memory-checking build (e.g. valgrind) the overflow is flagged directly.
792  */
793 static int test_kari_wrap_pad_unwrap_overflow(void)
794 {
795     /* DER encoding of the id-aes256-wrap OID (2.16.840.1.101.3.4.1.45). */
796     static const unsigned char aes256_wrap_oid[] = {
797         0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x01, 0x2d
798     };
799     int ret = 0;
800     X509 *eccert = NULL;
801     EVP_PKEY *eckey = NULL;
802     BIO *certbio = NULL, *keybio = NULL, *msgbio = NULL, *outbio = NULL;
803     STACK_OF(X509) *recips = NULL;
804     CMS_ContentInfo *cms = NULL, *cms2 = NULL;
805     unsigned char *der = NULL;
806     const unsigned char *p;
807     int derlen, i, patched = 0;
808     const char *msg = "secret content for kari";
809 
810     if ((certbio = BIO_new_file(ec_recip_in, "r")) == NULL
811         || PEM_read_bio_X509(certbio, &eccert, NULL, NULL) == NULL
812         || (keybio = BIO_new_file(ec_recip_in, "r")) == NULL
813         || PEM_read_bio_PrivateKey(keybio, &eckey, NULL, NULL) == NULL) {
814         goto end;
815     }
816 
817     if (!TEST_ptr(recips = sk_X509_new_null())
818         || !TEST_int_gt(sk_X509_push(recips, eccert), 0))
819         goto end;
820 
821     /* Build a normal ECDH KARI message; it uses non-padded id-aes256-wrap. */
822     if (!TEST_ptr(msgbio = BIO_new_mem_buf(msg, (int)strlen(msg)))
823         || !TEST_ptr(cms = CMS_encrypt(recips, msgbio, EVP_aes_256_cbc(),
824                          CMS_BINARY)))
825         goto end;
826 
827     if (!TEST_int_gt(derlen = i2d_CMS_ContentInfo(cms, &der), 0))
828         goto end;
829 
830     /* Swap id-aes256-wrap -> id-aes256-wrap-pad (0x2d -> 0x30). */
831     for (i = 0; i + (int)sizeof(aes256_wrap_oid) <= derlen; i++) {
832         if (memcmp(der + i, aes256_wrap_oid, sizeof(aes256_wrap_oid)) == 0) {
833             der[i + sizeof(aes256_wrap_oid) - 1] = 0x30;
834             patched = 1;
835             break;
836         }
837     }
838     if (!TEST_true(patched))
839         goto end;
840 
841     p = der;
842     if (!TEST_ptr(cms2 = d2i_CMS_ContentInfo(NULL, &p, derlen)))
843         goto end;
844 
845     /*
846      * The wrap-pad unwrap fails the AIV check; with the fix it does so without
847      * writing past the CMS-allocated buffer.  CMS_decrypt() must fail cleanly.
848      */
849     if (!TEST_ptr(outbio = BIO_new(BIO_s_mem()))
850         || !TEST_false(CMS_decrypt(cms2, eckey, eccert, NULL, outbio, 0)))
851         goto end;
852 
853     ret = 1;
854 end:
855     ERR_clear_error();
856     OPENSSL_free(der);
857     sk_X509_free(recips);
858     CMS_ContentInfo_free(cms);
859     CMS_ContentInfo_free(cms2);
860     BIO_free(certbio);
861     BIO_free(keybio);
862     BIO_free(msgbio);
863     BIO_free(outbio);
864     X509_free(eccert);
865     EVP_PKEY_free(eckey);
866     return ret;
867 }
868 #endif
869 
870 OPT_TEST_DECLARE_USAGE("certfile privkeyfile derfile tooLongIVpem pwriKekOobDer pwriKekNoIv ecrecip\n")
871 
872 int setup_tests(void)
873 {
874     char *certin = NULL, *privkeyin = NULL;
875     BIO *certbio = NULL, *privkeybio = NULL;
876 
877     if (!test_skip_common_options()) {
878         TEST_error("Error parsing test options\n");
879         return 0;
880     }
881 
882     if (!TEST_ptr(certin = test_get_argument(0))
883         || !TEST_ptr(privkeyin = test_get_argument(1))
884         || !TEST_ptr(derin = test_get_argument(2))
885         || !TEST_ptr(too_long_iv_cms_in = test_get_argument(3))
886         || !TEST_ptr(pwri_kek_oob_der_in = test_get_argument(4))
887         || !TEST_ptr(pwri_kek_no_iv_in = test_get_argument(5))
888         || !TEST_ptr(ec_recip_in = test_get_argument(6)))
889         return 0;
890 
891     certbio = BIO_new_file(certin, "r");
892     if (!TEST_ptr(certbio))
893         return 0;
894     if (!TEST_true(PEM_read_bio_X509(certbio, &cert, NULL, NULL))) {
895         BIO_free(certbio);
896         return 0;
897     }
898     BIO_free(certbio);
899 
900     privkeybio = BIO_new_file(privkeyin, "r");
901     if (!TEST_ptr(privkeybio)) {
902         X509_free(cert);
903         cert = NULL;
904         return 0;
905     }
906     if (!TEST_true(PEM_read_bio_PrivateKey(privkeybio, &privkey, NULL, NULL))) {
907         BIO_free(privkeybio);
908         X509_free(cert);
909         cert = NULL;
910         return 0;
911     }
912     BIO_free(privkeybio);
913 
914     ADD_TEST(test_encrypt_decrypt_aes_cbc);
915     ADD_TEST(test_encrypt_decrypt_aes_128_gcm);
916     ADD_TEST(test_encrypt_decrypt_aes_192_gcm);
917     ADD_TEST(test_encrypt_decrypt_aes_256_gcm);
918     ADD_TEST(test_non_aead_on_auth_envelope_enc);
919     ADD_TEST(test_non_aead_on_auth_envelope_dec);
920     ADD_TEST(test_short_mac_on_auth_envelope_data);
921     ADD_TEST(test_CMS_add1_cert);
922     ADD_TEST(test_d2i_CMS_bio_NULL);
923     ADD_TEST(test_CMS_set1_key_mem_leak);
924     ADD_TEST(test_encrypted_data);
925     ADD_TEST(test_encrypted_data_aead);
926     ADD_ALL_TESTS(test_d2i_CMS_decode, 2);
927     ADD_TEST(test_cms_aesgcm_iv_too_long);
928     ADD_TEST(test_pwri_kek_unwrap_short_encrypted_key);
929     ADD_TEST(test_pwri_kek_unwrap_no_iv_key);
930 
931 #ifndef OPENSSL_NO_EC
932     ADD_TEST(test_kari_wrap_pad_unwrap_overflow);
933 #endif
934     return 1;
935 }
936 
937 void cleanup_tests(void)
938 {
939     X509_free(cert);
940     EVP_PKEY_free(privkey);
941 }
942