1 /* $OpenBSD: asn1api.c,v 1.3 2022/07/09 14:47:42 tb Exp $ */
2 /*
3 * Copyright (c) 2021 Joel Sing <jsing@openbsd.org>
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
18 #include <openssl/asn1.h>
19 #include <openssl/err.h>
20
21 #include <err.h>
22 #include <stdio.h>
23 #include <string.h>
24
25 const long asn1_tag2bits[] = {
26 [0] = 0,
27 [1] = 0,
28 [2] = 0,
29 [3] = B_ASN1_BIT_STRING,
30 [4] = B_ASN1_OCTET_STRING,
31 [5] = 0,
32 [6] = 0,
33 [7] = B_ASN1_UNKNOWN,
34 [8] = B_ASN1_UNKNOWN,
35 [9] = B_ASN1_UNKNOWN,
36 [10] = B_ASN1_UNKNOWN,
37 [11] = B_ASN1_UNKNOWN,
38 [12] = B_ASN1_UTF8STRING,
39 [13] = B_ASN1_UNKNOWN,
40 [14] = B_ASN1_UNKNOWN,
41 [15] = B_ASN1_UNKNOWN,
42 [16] = B_ASN1_SEQUENCE,
43 [17] = 0,
44 [18] = B_ASN1_NUMERICSTRING,
45 [19] = B_ASN1_PRINTABLESTRING,
46 [20] = B_ASN1_T61STRING,
47 [21] = B_ASN1_VIDEOTEXSTRING,
48 [22] = B_ASN1_IA5STRING,
49 [23] = B_ASN1_UTCTIME,
50 [24] = B_ASN1_GENERALIZEDTIME,
51 [25] = B_ASN1_GRAPHICSTRING,
52 [26] = B_ASN1_ISO64STRING,
53 [27] = B_ASN1_GENERALSTRING,
54 [28] = B_ASN1_UNIVERSALSTRING,
55 [29] = B_ASN1_UNKNOWN,
56 [30] = B_ASN1_BMPSTRING,
57 };
58
59 static int
asn1_tag2bit(void)60 asn1_tag2bit(void)
61 {
62 int failed = 1;
63 long bit;
64 int i;
65
66 for (i = -3; i <= V_ASN1_NEG + 30; i++) {
67 bit = ASN1_tag2bit(i);
68 if (i >= 0 && i <= 30) {
69 if (bit != asn1_tag2bits[i]) {
70 fprintf(stderr, "FAIL: ASN1_tag2bit(%d) = 0x%lx,"
71 " want 0x%lx\n", i, bit, asn1_tag2bits[i]);
72 goto failed;
73 }
74 } else {
75 if (bit != 0) {
76 fprintf(stderr, "FAIL: ASN1_tag2bit(%d) = 0x%lx,"
77 " want 0x0\n", i, bit);
78 goto failed;
79 }
80 }
81 }
82
83 failed = 0;
84
85 failed:
86 return failed;
87 }
88
89 static int
asn1_tag2str(void)90 asn1_tag2str(void)
91 {
92 int failed = 1;
93 const char *s;
94 int i;
95
96 for (i = -3; i <= V_ASN1_NEG + 30; i++) {
97 if ((s = ASN1_tag2str(i)) == NULL) {
98 fprintf(stderr, "FAIL: ASN1_tag2str(%d) returned "
99 "NULL\n", i);
100 goto failed;
101 }
102 if ((i >= 0 && i <= 30) || i == V_ASN1_NEG_INTEGER ||
103 i == V_ASN1_NEG_ENUMERATED) {
104 if (strcmp(s, "(unknown)") == 0) {
105 fprintf(stderr, "FAIL: ASN1_tag2str(%d) = '%s',"
106 " want tag name\n", i, s);
107 goto failed;
108 }
109 } else {
110 if (strcmp(s, "(unknown)") != 0) {
111 fprintf(stderr, "FAIL: ASN1_tag2str(%d) = '%s',"
112 " want '(unknown')\n", i, s);
113 goto failed;
114 }
115 }
116 }
117
118 failed = 0;
119
120 failed:
121 return failed;
122 }
123
124 struct asn1_get_object_test {
125 const uint8_t asn1[64];
126 size_t asn1_len;
127 size_t asn1_hdr_len;
128 int want_ret;
129 long want_length;
130 int want_tag;
131 int want_class;
132 int want_error;
133 };
134
135 const struct asn1_get_object_test asn1_get_object_tests[] = {
136 {
137 /* Zero tag and zero length (EOC). */
138 .asn1 = {0x00, 0x00},
139 .asn1_len = 2,
140 .asn1_hdr_len = 2,
141 .want_ret = 0x00,
142 .want_length = 0,
143 .want_tag = 0,
144 .want_class = 0,
145 },
146 {
147 /* Boolean with short form length. */
148 .asn1 = {0x01, 0x01},
149 .asn1_len = 3,
150 .asn1_hdr_len = 2,
151 .want_ret = 0x00,
152 .want_length = 1,
153 .want_tag = 1,
154 .want_class = 0,
155 },
156 {
157 /* Long form tag. */
158 .asn1 = {0x1f, 0x7f, 0x01},
159 .asn1_len = 3 + 128,
160 .asn1_hdr_len = 3,
161 .want_ret = 0x00,
162 .want_length = 1,
163 .want_tag = 127,
164 .want_class = 0,
165 },
166 {
167 /* Long form tag with class application. */
168 .asn1 = {0x5f, 0x7f, 0x01},
169 .asn1_len = 3 + 128,
170 .asn1_hdr_len = 3,
171 .want_ret = 0x00,
172 .want_length = 1,
173 .want_tag = 127,
174 .want_class = 1 << 6,
175 },
176 {
177 /* Long form tag with class context-specific. */
178 .asn1 = {0x9f, 0x7f, 0x01},
179 .asn1_len = 3 + 128,
180 .asn1_hdr_len = 3,
181 .want_ret = 0x00,
182 .want_length = 1,
183 .want_tag = 127,
184 .want_class = 2 << 6,
185 },
186 {
187 /* Long form tag with class private. */
188 .asn1 = {0xdf, 0x7f, 0x01},
189 .asn1_len = 3 + 128,
190 .asn1_hdr_len = 3,
191 .want_ret = 0x00,
192 .want_length = 1,
193 .want_tag = 127,
194 .want_class = 3 << 6,
195 },
196 {
197 /* Long form tag (maximum). */
198 .asn1 = {0x1f, 0x87, 0xff, 0xff, 0xff, 0x7f, 0x01},
199 .asn1_len = 8,
200 .asn1_hdr_len = 7,
201 .want_ret = 0x00,
202 .want_length = 1,
203 .want_tag = 0x7fffffff,
204 .want_class = 0,
205 },
206 {
207 /* Long form tag (maximum + 1). */
208 .asn1 = {0x1f, 0x88, 0x80, 0x80, 0x80, 0x00, 0x01},
209 .asn1_len = 8,
210 .asn1_hdr_len = 7,
211 .want_ret = 0x80,
212 .want_error = ASN1_R_HEADER_TOO_LONG,
213 },
214 {
215 /* OctetString with long form length. */
216 .asn1 = {0x04, 0x81, 0x80},
217 .asn1_len = 3 + 128,
218 .asn1_hdr_len = 3,
219 .want_ret = 0x00,
220 .want_length = 128,
221 .want_tag = 4,
222 .want_class = 0,
223 },
224 {
225 /* OctetString with long form length. */
226 .asn1 = {0x04, 0x84, 0x7f, 0xff, 0xff, 0xf9},
227 .asn1_len = 0x7fffffff,
228 .asn1_hdr_len = 6,
229 .want_ret = 0x00,
230 .want_length = 0x7ffffff9,
231 .want_tag = 4,
232 .want_class = 0,
233 },
234 {
235 /* Long form tag and long form length. */
236 .asn1 = {0x1f, 0x87, 0xff, 0xff, 0xff, 0x7f, 0x84, 0x7f, 0xff, 0xff, 0xf4},
237 .asn1_len = 0x7fffffff,
238 .asn1_hdr_len = 11,
239 .want_ret = 0x00,
240 .want_length = 0x7ffffff4,
241 .want_tag = 0x7fffffff,
242 .want_class = 0,
243 },
244 {
245 /* Constructed OctetString with definite length. */
246 .asn1 = {0x24, 0x03},
247 .asn1_len = 5,
248 .asn1_hdr_len = 2,
249 .want_ret = 0x20,
250 .want_length = 3,
251 .want_tag = 4,
252 .want_class = 0,
253 },
254 {
255 /* Constructed OctetString with indefinite length. */
256 .asn1 = {0x24, 0x80},
257 .asn1_len = 5,
258 .asn1_hdr_len = 2,
259 .want_ret = 0x21,
260 .want_length = 0,
261 .want_tag = 4,
262 .want_class = 0,
263 },
264 {
265 /* Boolean with indefinite length (invalid). */
266 .asn1 = {0x01, 0x80},
267 .asn1_len = 3,
268 .want_ret = 0x80,
269 .want_error = ASN1_R_HEADER_TOO_LONG,
270 },
271 {
272 /* OctetString with insufficient data (only tag). */
273 .asn1 = {0x04, 0x04},
274 .asn1_len = 1,
275 .want_ret = 0x80,
276 .want_error = ASN1_R_HEADER_TOO_LONG,
277 },
278 {
279 /* OctetString with insufficient data (missing content). */
280 .asn1 = {0x04, 0x04},
281 .asn1_len = 2,
282 .asn1_hdr_len = 2,
283 .want_ret = 0x80,
284 .want_length = 4,
285 .want_tag = 4,
286 .want_class = 0,
287 .want_error = ASN1_R_TOO_LONG,
288 },
289 {
290 /* OctetString with insufficient data (partial content). */
291 .asn1 = {0x04, 0x04},
292 .asn1_len = 5,
293 .asn1_hdr_len = 2,
294 .want_ret = 0x80,
295 .want_length = 4,
296 .want_tag = 4,
297 .want_class = 0,
298 .want_error = ASN1_R_TOO_LONG,
299 },
300 {
301 /* Constructed OctetString with insufficient data (only tag/len). */
302 .asn1 = {0x24, 0x04},
303 .asn1_len = 2,
304 .asn1_hdr_len = 2,
305 .want_ret = 0xa0,
306 .want_length = 4,
307 .want_tag = 4,
308 .want_class = 0,
309 .want_error = ASN1_R_TOO_LONG,
310 },
311 };
312
313 #define N_ASN1_GET_OBJECT_TESTS \
314 (sizeof(asn1_get_object_tests) / sizeof(*asn1_get_object_tests))
315
316 static int
asn1_get_object(void)317 asn1_get_object(void)
318 {
319 const struct asn1_get_object_test *agot;
320 const uint8_t *p;
321 int ret, tag, tag_class;
322 long err, length;
323 size_t i;
324 int failed = 1;
325
326 for (i = 0; i < N_ASN1_GET_OBJECT_TESTS; i++) {
327 agot = &asn1_get_object_tests[i];
328
329 ERR_clear_error();
330
331 p = agot->asn1;
332 ret = ASN1_get_object(&p, &length, &tag, &tag_class, agot->asn1_len);
333
334 if (ret != agot->want_ret) {
335 fprintf(stderr, "FAIL: %zu - got return value %x, want %x\n",
336 i, ret, agot->want_ret);
337 goto failed;
338 }
339 if (ret & 0x80) {
340 err = ERR_peek_error();
341 if (ERR_GET_REASON(err) != agot->want_error) {
342 fprintf(stderr, "FAIL: %zu - got error reason %d, "
343 "want %d\n", i, ERR_GET_REASON(err),
344 agot->want_error);
345 goto failed;
346 }
347 if (ERR_GET_REASON(err) == ASN1_R_HEADER_TOO_LONG) {
348 if (p != agot->asn1) {
349 fprintf(stderr, "FAIL: %zu - got ber_in %p, "
350 "want %p\n", i, p, agot->asn1);
351 goto failed;
352 }
353 continue;
354 }
355 }
356 if (length != agot->want_length) {
357 fprintf(stderr, "FAIL: %zu - got length %ld, want %ld\n",
358 i, length, agot->want_length);
359 goto failed;
360 }
361 if (tag != agot->want_tag) {
362 fprintf(stderr, "FAIL: %zu - got tag %d, want %d\n",
363 i, tag, agot->want_tag);
364 goto failed;
365 }
366 if (tag_class != agot->want_class) {
367 fprintf(stderr, "FAIL: %zu - got class %d, want %d\n",
368 i, tag_class, agot->want_class);
369 goto failed;
370 }
371 if (p != agot->asn1 + agot->asn1_hdr_len) {
372 fprintf(stderr, "FAIL: %zu - got ber_in %p, want %p\n",
373 i, p, agot->asn1 + agot->asn1_len);
374 goto failed;
375 }
376 }
377
378 failed = 0;
379
380 failed:
381 return failed;
382 }
383
384 static int
asn1_integer_get_null_test(void)385 asn1_integer_get_null_test(void)
386 {
387 int failed = 0;
388 long ret;
389
390 if ((ret = ASN1_INTEGER_get(NULL)) != 0) {
391 fprintf(stderr, "FAIL: ASN1_INTEGER_get(NULL) %ld != 0\n", ret);
392 failed |= 1;
393 }
394
395 if ((ret = ASN1_ENUMERATED_get(NULL)) != 0) {
396 fprintf(stderr, "FAIL: ASN1_ENUMERATED_get(NULL) %ld != 0\n",
397 ret);
398 failed |= 1;
399 }
400
401 return failed;
402 }
403
404 int
main(int argc,char ** argv)405 main(int argc, char **argv)
406 {
407 int failed = 0;
408
409 failed |= asn1_tag2bit();
410 failed |= asn1_tag2str();
411 failed |= asn1_get_object();
412 failed |= asn1_integer_get_null_test();
413
414 return (failed);
415 }
416