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