xref: /freebsd/crypto/openssl/test/asn1_decode_test.c (revision 78e936b2d0b5e6554425009199be31e76bc67c10)
1 /*
2  * Copyright 2017-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 <stdio.h>
11 #include <string.h>
12 
13 #include <openssl/rand.h>
14 #include <openssl/asn1.h>
15 #include <openssl/asn1t.h>
16 #include <openssl/obj_mac.h>
17 #include <openssl/bio.h>
18 #include <openssl/buffer.h>
19 #include <openssl/err.h>
20 #include "internal/numbers.h"
21 #include "internal/asn1.h"
22 #include "testutil.h"
23 
24 #ifdef __GNUC__
25 #pragma GCC diagnostic ignored "-Wunused-function"
26 #endif
27 #ifdef __clang__
28 #pragma clang diagnostic ignored "-Wunused-function"
29 #endif
30 
31 /* Badly coded ASN.1 INTEGER zero wrapped in a sequence */
32 static unsigned char t_invalid_zero[] = {
33     0x30, 0x02, /* SEQUENCE tag + length */
34     0x02, 0x00 /* INTEGER tag + length */
35 };
36 
37 #ifndef OPENSSL_NO_DEPRECATED_3_0
38 /* LONG case ************************************************************* */
39 
40 typedef struct {
41     long test_long;
42 } ASN1_LONG_DATA;
43 
44 ASN1_SEQUENCE(ASN1_LONG_DATA) = {
45     ASN1_EMBED(ASN1_LONG_DATA, test_long, LONG),
46 } static_ASN1_SEQUENCE_END(ASN1_LONG_DATA)
47 
48 IMPLEMENT_STATIC_ASN1_ENCODE_FUNCTIONS(ASN1_LONG_DATA)
49 IMPLEMENT_STATIC_ASN1_ALLOC_FUNCTIONS(ASN1_LONG_DATA)
50 
51 static int test_long(void)
52 {
53     const unsigned char *p = t_invalid_zero;
54     ASN1_LONG_DATA *dectst = d2i_ASN1_LONG_DATA(NULL, &p, sizeof(t_invalid_zero));
55 
56     if (dectst == NULL)
57         return 0; /* Fail */
58 
59     ASN1_LONG_DATA_free(dectst);
60     return 1;
61 }
62 #endif
63 
64 /* INT32 case ************************************************************* */
65 
66 typedef struct {
67     int32_t test_int32;
68 } ASN1_INT32_DATA;
69 
70 ASN1_SEQUENCE(ASN1_INT32_DATA) = {
71     ASN1_EMBED(ASN1_INT32_DATA, test_int32, INT32),
72 } static_ASN1_SEQUENCE_END(ASN1_INT32_DATA)
73 
74 IMPLEMENT_STATIC_ASN1_ENCODE_FUNCTIONS(ASN1_INT32_DATA)
75 IMPLEMENT_STATIC_ASN1_ALLOC_FUNCTIONS(ASN1_INT32_DATA)
76 
77 static int test_int32(void)
78 {
79     const unsigned char *p = t_invalid_zero;
80     ASN1_INT32_DATA *dectst = d2i_ASN1_INT32_DATA(NULL, &p, sizeof(t_invalid_zero));
81 
82     if (dectst == NULL)
83         return 0; /* Fail */
84 
85     ASN1_INT32_DATA_free(dectst);
86     return 1;
87 }
88 
89 /* UINT32 case ************************************************************* */
90 
91 typedef struct {
92     uint32_t test_uint32;
93 } ASN1_UINT32_DATA;
94 
95 ASN1_SEQUENCE(ASN1_UINT32_DATA) = {
96     ASN1_EMBED(ASN1_UINT32_DATA, test_uint32, UINT32),
97 } static_ASN1_SEQUENCE_END(ASN1_UINT32_DATA)
98 
99 IMPLEMENT_STATIC_ASN1_ENCODE_FUNCTIONS(ASN1_UINT32_DATA)
100 IMPLEMENT_STATIC_ASN1_ALLOC_FUNCTIONS(ASN1_UINT32_DATA)
101 
102 static int test_uint32(void)
103 {
104     const unsigned char *p = t_invalid_zero;
105     ASN1_UINT32_DATA *dectst = d2i_ASN1_UINT32_DATA(NULL, &p, sizeof(t_invalid_zero));
106 
107     if (dectst == NULL)
108         return 0; /* Fail */
109 
110     ASN1_UINT32_DATA_free(dectst);
111     return 1;
112 }
113 
114 /* INT64 case ************************************************************* */
115 
116 typedef struct {
117     int64_t test_int64;
118 } ASN1_INT64_DATA;
119 
120 ASN1_SEQUENCE(ASN1_INT64_DATA) = {
121     ASN1_EMBED(ASN1_INT64_DATA, test_int64, INT64),
122 } static_ASN1_SEQUENCE_END(ASN1_INT64_DATA)
123 
124 IMPLEMENT_STATIC_ASN1_ENCODE_FUNCTIONS(ASN1_INT64_DATA)
125 IMPLEMENT_STATIC_ASN1_ALLOC_FUNCTIONS(ASN1_INT64_DATA)
126 
127 static int test_int64(void)
128 {
129     const unsigned char *p = t_invalid_zero;
130     ASN1_INT64_DATA *dectst = d2i_ASN1_INT64_DATA(NULL, &p, sizeof(t_invalid_zero));
131 
132     if (dectst == NULL)
133         return 0; /* Fail */
134 
135     ASN1_INT64_DATA_free(dectst);
136     return 1;
137 }
138 
139 /* UINT64 case ************************************************************* */
140 
141 typedef struct {
142     uint64_t test_uint64;
143 } ASN1_UINT64_DATA;
144 
145 ASN1_SEQUENCE(ASN1_UINT64_DATA) = {
146     ASN1_EMBED(ASN1_UINT64_DATA, test_uint64, UINT64),
147 } static_ASN1_SEQUENCE_END(ASN1_UINT64_DATA)
148 
149 IMPLEMENT_STATIC_ASN1_ENCODE_FUNCTIONS(ASN1_UINT64_DATA)
150 IMPLEMENT_STATIC_ASN1_ALLOC_FUNCTIONS(ASN1_UINT64_DATA)
151 
152 static int test_uint64(void)
153 {
154     const unsigned char *p = t_invalid_zero;
155     ASN1_UINT64_DATA *dectst = d2i_ASN1_UINT64_DATA(NULL, &p, sizeof(t_invalid_zero));
156 
157     if (dectst == NULL)
158         return 0; /* Fail */
159 
160     ASN1_UINT64_DATA_free(dectst);
161     return 1;
162 }
163 
164 /* GeneralizedTime underflow *********************************************** */
165 
test_gentime(void)166 static int test_gentime(void)
167 {
168     /* Underflowing GeneralizedTime 161208193400Z (YYMMDDHHMMSSZ) */
169     const unsigned char der[] = {
170         0x18, 0x0d, 0x31, 0x36, 0x31, 0x32, 0x30, 0x38, 0x31, 0x39,
171         0x33, 0x34, 0x30, 0x30, 0x5a
172     };
173     const unsigned char *p;
174     int der_len, rc = 1;
175     ASN1_GENERALIZEDTIME *gentime;
176 
177     p = der;
178     der_len = sizeof(der);
179     gentime = d2i_ASN1_GENERALIZEDTIME(NULL, &p, der_len);
180 
181     if (!TEST_ptr_null(gentime))
182         rc = 0; /* fail */
183 
184     ASN1_GENERALIZEDTIME_free(gentime);
185     return rc;
186 }
187 
188 /* UTCTime underflow ******************************************************* */
189 
test_utctime(void)190 static int test_utctime(void)
191 {
192     /* Underflowing UTCTime 0205104700Z (MMDDHHMMSSZ) */
193     const unsigned char der[] = {
194         0x17, 0x0b, 0x30, 0x32, 0x30, 0x35, 0x31, 0x30, 0x34, 0x37,
195         0x30, 0x30, 0x5a
196     };
197     const unsigned char *p;
198     int der_len, rc = 1;
199     ASN1_UTCTIME *utctime;
200 
201     p = der;
202     der_len = sizeof(der);
203     utctime = d2i_ASN1_UTCTIME(NULL, &p, der_len);
204 
205     if (!TEST_ptr_null(utctime))
206         rc = 0; /* fail */
207 
208     ASN1_UTCTIME_free(utctime);
209     return rc;
210 }
211 
212 /* Invalid template ******************************************************** */
213 
214 typedef struct {
215     ASN1_STRING *invalidDirString;
216 } INVALIDTEMPLATE;
217 
218 ASN1_SEQUENCE(INVALIDTEMPLATE) = {
219     /*
220      * DirectoryString is a CHOICE type so it must use explicit tagging -
221      * but we deliberately use implicit here, which makes this template invalid.
222      */
223     ASN1_IMP(INVALIDTEMPLATE, invalidDirString, DIRECTORYSTRING, 12)
224 } static_ASN1_SEQUENCE_END(INVALIDTEMPLATE)
225 
226 IMPLEMENT_STATIC_ASN1_ENCODE_FUNCTIONS(INVALIDTEMPLATE)
227 IMPLEMENT_STATIC_ASN1_ALLOC_FUNCTIONS(INVALIDTEMPLATE)
228 
229 /* Empty sequence for invalid template test */
230 static unsigned char t_invalid_template[] = {
231     0x30, 0x03, /* SEQUENCE tag + length */
232     0x0c, 0x01, 0x41 /* UTF8String, length 1, "A" */
233 };
234 
test_invalid_template(void)235 static int test_invalid_template(void)
236 {
237     const unsigned char *p = t_invalid_template;
238     INVALIDTEMPLATE *tmp = d2i_INVALIDTEMPLATE(NULL, &p,
239         sizeof(t_invalid_template));
240 
241     /* We expect a NULL pointer return */
242     if (TEST_ptr_null(tmp))
243         return 1;
244 
245     INVALIDTEMPLATE_free(tmp);
246     return 0;
247 }
248 
test_reuse_asn1_object(void)249 static int test_reuse_asn1_object(void)
250 {
251     static unsigned char cn_der[] = { 0x06, 0x03, 0x55, 0x04, 0x06 };
252     static unsigned char oid_der[] = {
253         0x06, 0x06, 0x2a, 0x03, 0x04, 0x05, 0x06, 0x07
254     };
255     int ret = 0;
256     ASN1_OBJECT *obj;
257     unsigned char const *p = oid_der;
258 
259     /* Create an object that owns dynamically allocated 'sn' and 'ln' fields */
260 
261     if (!TEST_ptr(obj = ASN1_OBJECT_create(NID_undef, cn_der, sizeof(cn_der),
262                       "C", "countryName")))
263         goto err;
264     /* reuse obj - this should not leak sn and ln */
265     if (!TEST_ptr(d2i_ASN1_OBJECT(&obj, &p, sizeof(oid_der))))
266         goto err;
267     ret = 1;
268 err:
269     ASN1_OBJECT_free(obj);
270     return ret;
271 }
272 
273 /*
274  * A minimal, complete DER object: SEQUENCE { INTEGER 0 }.
275  * asn1_d2i_read_bio() should consume exactly these bytes.
276  */
277 static const unsigned char one_obj[] = {
278     0x30, 0x03, /* SEQUENCE, length 3 */
279     0x02, 0x01, 0x00 /*   INTEGER 0        */
280 };
281 
282 /*
283  * Reading concatenated DER objects from a BIO must stop cleanly at EOF:
284  * once the input is exhausted on an object boundary, asn1_d2i_read_bio()
285  * returns < 0 and must NOT leave an error on the queue.  Callers that loop
286  * over concatenated values (e.g. CPython's ssl module loading the Windows
287  * certificate store via d2i_X509_bio()) rely on this to detect end-of-input;
288  * a spurious ASN1_R_NOT_ENOUGH_DATA there is reported as a fatal error.
289  */
test_d2i_read_bio_clean_eof(void)290 static int test_d2i_read_bio_clean_eof(void)
291 {
292     unsigned char two_objs[sizeof(one_obj) * 2];
293     BIO *bio = NULL;
294     BUF_MEM *buf = NULL;
295     int ret = 0;
296 
297     memcpy(two_objs, one_obj, sizeof(one_obj));
298     memcpy(two_objs + sizeof(one_obj), one_obj, sizeof(one_obj));
299 
300     if (!TEST_ptr(bio = BIO_new_mem_buf(two_objs, sizeof(two_objs))))
301         goto err;
302     ERR_clear_error();
303 
304     /* Both complete objects are read, one per call. */
305     if (!TEST_int_eq(asn1_d2i_read_bio(bio, &buf), (int)sizeof(one_obj)))
306         goto err;
307     BUF_MEM_free(buf);
308     buf = NULL;
309     if (!TEST_int_eq(asn1_d2i_read_bio(bio, &buf), (int)sizeof(one_obj)))
310         goto err;
311     BUF_MEM_free(buf);
312     buf = NULL;
313 
314     /* Clean EOF: failure return, but no error must be queued. */
315     if (!TEST_int_lt(asn1_d2i_read_bio(bio, &buf), 0))
316         goto err;
317     if (!TEST_ulong_eq(ERR_peek_error(), 0))
318         goto err;
319 
320     ret = 1;
321 err:
322     BUF_MEM_free(buf);
323     BIO_free(bio);
324     return ret;
325 }
326 
327 /*
328  * In contrast, hitting EOF in the middle of an object is genuine truncation
329  * and must still be reported as ASN1_R_NOT_ENOUGH_DATA.
330  */
test_d2i_read_bio_truncated(void)331 static int test_d2i_read_bio_truncated(void)
332 {
333     static const unsigned char truncated[] = {
334         0x30, 0x05, /* SEQUENCE claims 5 content bytes ... */
335         0x02, 0x01 /* ... but only 2 are present         */
336     };
337     BIO *bio = NULL;
338     BUF_MEM *buf = NULL;
339     unsigned long e;
340     int ret = 0;
341 
342     if (!TEST_ptr(bio = BIO_new_mem_buf(truncated, sizeof(truncated))))
343         goto err;
344     ERR_clear_error();
345 
346     if (!TEST_int_lt(asn1_d2i_read_bio(bio, &buf), 0))
347         goto err;
348     e = ERR_peek_last_error();
349     if (!TEST_int_eq(ERR_GET_LIB(e), ERR_LIB_ASN1)
350         || !TEST_int_eq(ERR_GET_REASON(e), ASN1_R_NOT_ENOUGH_DATA))
351         goto err;
352 
353     ret = 1;
354 err:
355     BUF_MEM_free(buf);
356     BIO_free(bio);
357     return ret;
358 }
359 
360 /*
361  * An EOF reached while still inside an indefinite-length constructed value,
362  * before its end-of-contents octets, is truncation too (not a clean boundary),
363  * so it must also report ASN1_R_NOT_ENOUGH_DATA rather than an empty queue.
364  */
test_d2i_read_bio_indefinite_truncated(void)365 static int test_d2i_read_bio_indefinite_truncated(void)
366 {
367     /* SEQUENCE (indefinite) { INTEGER 0 } with the 00 00 EOC missing */
368     static const unsigned char truncated_indefinite[] = {
369         0x30, 0x80, /* SEQUENCE, indefinite length */
370         0x02, 0x01, 0x00 /* INTEGER 0; no end-of-contents octets follow */
371     };
372     BIO *bio = NULL;
373     BUF_MEM *buf = NULL;
374     unsigned long e;
375     int ret = 0;
376 
377     bio = BIO_new_mem_buf(truncated_indefinite, sizeof(truncated_indefinite));
378     if (!TEST_ptr(bio))
379         goto err;
380     ERR_clear_error();
381 
382     if (!TEST_int_lt(asn1_d2i_read_bio(bio, &buf), 0))
383         goto err;
384     e = ERR_peek_last_error();
385     if (!TEST_int_eq(ERR_GET_LIB(e), ERR_LIB_ASN1)
386         || !TEST_int_eq(ERR_GET_REASON(e), ASN1_R_NOT_ENOUGH_DATA))
387         goto err;
388 
389     ret = 1;
390 err:
391     BUF_MEM_free(buf);
392     BIO_free(bio);
393     return ret;
394 }
395 
396 /*
397  * An EOF reached part-way through an object's header, with some header bytes
398  * already buffered, is truncation as well.  This exercises the "diff != 0" arm
399  * of the header-read check (distinct from the body read handled elsewhere).
400  */
test_d2i_read_bio_partial_header(void)401 static int test_d2i_read_bio_partial_header(void)
402 {
403     /* SEQUENCE with a 2-byte long-form length, but only one length byte given */
404     static const unsigned char partial_header[] = {
405         0x30, 0x82, 0x01 /* SEQUENCE, length declared as 2 bytes, 1 present */
406     };
407     BIO *bio = NULL;
408     BUF_MEM *buf = NULL;
409     unsigned long e;
410     int ret = 0;
411 
412     if (!TEST_ptr(bio = BIO_new_mem_buf(partial_header, sizeof(partial_header))))
413         goto err;
414     ERR_clear_error();
415 
416     if (!TEST_int_lt(asn1_d2i_read_bio(bio, &buf), 0))
417         goto err;
418     e = ERR_peek_last_error();
419     if (!TEST_int_eq(ERR_GET_LIB(e), ERR_LIB_ASN1)
420         || !TEST_int_eq(ERR_GET_REASON(e), ASN1_R_NOT_ENOUGH_DATA))
421         goto err;
422 
423     ret = 1;
424 err:
425     BUF_MEM_free(buf);
426     BIO_free(bio);
427     return ret;
428 }
429 
setup_tests(void)430 int setup_tests(void)
431 {
432 #ifndef OPENSSL_NO_DEPRECATED_3_0
433     ADD_TEST(test_long);
434 #endif
435     ADD_TEST(test_int32);
436     ADD_TEST(test_uint32);
437     ADD_TEST(test_int64);
438     ADD_TEST(test_uint64);
439     ADD_TEST(test_gentime);
440     ADD_TEST(test_utctime);
441     ADD_TEST(test_invalid_template);
442     ADD_TEST(test_reuse_asn1_object);
443     ADD_TEST(test_d2i_read_bio_clean_eof);
444     ADD_TEST(test_d2i_read_bio_truncated);
445     ADD_TEST(test_d2i_read_bio_indefinite_truncated);
446     ADD_TEST(test_d2i_read_bio_partial_header);
447     return 1;
448 }
449