1/*	$OpenBSD: asn1oct.c,v 1.4 2023/05/13 07:17:32 tb Exp $ */
2
3/*
4 * Copyright (c) 2023 Theo Buehler <tb@openbsd.org>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19#include <assert.h>
20#include <err.h>
21#include <string.h>
22
23#include <openssl/asn1.h>
24#include <openssl/x509v3.h>
25
26#define TESTBUFFER_SIZE		20
27
28static const struct i2s_asn1_octet_string_test {
29	const char *desc;
30	const uint8_t buf[TESTBUFFER_SIZE];
31	long len;
32	const char *want;
33} i2s_test[] = {
34	{
35		.desc = "Empty buffer gives empty string",
36		.buf = { 0x00, },
37		.len = 0,
38		.want = "",
39	},
40	{
41		.desc = "all hex digits",
42		.buf = { 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, },
43		.len = 8,
44		.want = "01:23:45:67:89:AB:CD:EF",
45	},
46	{
47		.desc = "all hex digits, scrambled",
48		.buf = { 0x98, 0x24, 0xbf, 0x3a, 0xc7, 0xd6, 0x01, 0x5e, },
49		.len = 8,
50		.want = "98:24:BF:3A:C7:D6:01:5E",
51	},
52	{
53		.desc = "Embedded 0 byte",
54		.buf = { 0x7a, 0x00, 0xbb, },
55		.len = 3,
56		.want = "7A:00:BB",
57	},
58	{
59		.desc = "All zeroes",
60		.buf = { 0x00, 0x00, 0x00, 0x00, 0x00, },
61		.len = 4,
62		.want = "00:00:00:00",
63	},
64	{
65		.desc = "All bits set",
66		.buf = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, },
67		.len = 8,
68		.want = "FF:FF:FF:FF:FF:FF:FF:FF",
69	},
70	{
71		.desc = "negative length",
72		.buf = { 0x00, },
73		.len = -1,
74	},
75};
76
77#define N_I2S_TESTS (sizeof(i2s_test) / sizeof(i2s_test[0]))
78
79static int
80test_i2s_ASN1_OCTET_STRING(const struct i2s_asn1_octet_string_test *test)
81{
82	ASN1_OCTET_STRING *aos = NULL;
83	int should_fail = test->want == NULL;
84	char *got = NULL;
85	int failed = 0;
86
87	if ((aos = ASN1_OCTET_STRING_new()) == NULL)
88		errx(1, "ASN1_OCTET_STRING_new");
89
90	if (!ASN1_STRING_set(aos, (void *)test->buf, test->len))
91		errx(1, "ASN1_STRING_set");
92
93	if ((got = i2s_ASN1_OCTET_STRING(NULL, aos)) == NULL) {
94		if (!should_fail)
95			errx(1, "i2s_ASN1_OCTET_STRING");
96	}
97
98	if (!should_fail && strcmp(test->want, got) != 0) {
99		fprintf(stderr, "%s: \"%s\" failed: want \"%s\", got \"%s\"\n",
100		    __func__, test->desc, test->want, got);
101		failed |= 1;
102	}
103
104	ASN1_OCTET_STRING_free(aos);
105	free(got);
106
107	return failed;
108}
109
110static int
111test_new_ASN1_OCTET_STRING(void)
112{
113	ASN1_OCTET_STRING *aos = NULL;
114	char *got;
115	int failed = 0;
116
117	if ((aos = ASN1_OCTET_STRING_new()) == NULL)
118		errx(1, "%s: ASN1_OCTET_STRING_new", __func__);
119	if ((got = i2s_ASN1_OCTET_STRING(NULL, aos)) == NULL)
120		errx(1, "%s: i2s_ASN1_OCTET_STRING", __func__);
121
122	if (strcmp("", got) != 0) {
123		fprintf(stderr, "%s failed: want \"\", got \"%s\"\n",
124		    __func__, got);
125		failed |= 1;
126	}
127
128	ASN1_OCTET_STRING_free(aos);
129	free(got);
130
131	return failed;
132}
133
134static int
135run_i2s_ASN1_OCTET_STRING_tests(void)
136{
137	size_t i;
138	int failed = 0;
139
140	failed |= test_new_ASN1_OCTET_STRING();
141
142	for (i = 0; i < N_I2S_TESTS; i++)
143		failed |= test_i2s_ASN1_OCTET_STRING(&i2s_test[i]);
144
145	return failed;
146}
147
148static const struct s2i_asn1_octet_string_test {
149	const char *desc;
150	const char *in;
151	const char *want;
152} s2i_test[] = {
153	/* Tests that should succeed. */
154	{
155		.desc = "empty string",
156		.in = "",
157		.want = "",
158	},
159	{
160		.desc = "only colons",
161		.in = ":::::::",
162		.want = "",
163	},
164	{
165		.desc = "a 0 octet",
166		.in = "00",
167		.want = "00",
168	},
169	{
170		.desc = "a 0 octet with stupid colons",
171		.in = ":::00:::::",
172		.want = "00",
173	},
174	{
175		.desc = "more stupid colons",
176		.in = ":::C0fF::Ee:::::",
177		.want = "C0:FF:EE",
178	},
179	{
180		.desc = "all hex digits",
181		.in = "0123456789abcdef",
182		.want = "01:23:45:67:89:AB:CD:EF",
183	},
184
185	/* Tests that should fail. */
186	{
187		.desc = "colons between hex digits",
188		.in = "A:F",
189	},
190	{
191		.desc = "more colons between hex digits",
192		.in = "5:7",
193	},
194	{
195		.desc = "one hex digit",
196		.in = "1",
197	},
198	{
199		.desc = "three hex digits",
200		.in = "bad",
201	},
202	{
203		.desc = "three hex digits, colon after first digit",
204		.in = "b:ad",
205	},
206	{
207		.desc = "three hex digits, colon after second digit",
208		.in = "ba:d",
209	},
210	{
211		.desc = "non-hex digit",
212		.in = "g00d",
213	},
214	{
215		.desc = "non-hex digits",
216		.in = "d0gged",
217	},
218	{
219		.desc = "trailing non-hex digit",
220		.in = "d00der",
221	},
222};
223
224#define N_S2I_TESTS (sizeof(s2i_test) / sizeof(s2i_test[0]))
225
226static int
227test_s2i_ASN1_OCTET_STRING(const struct s2i_asn1_octet_string_test *test)
228{
229	ASN1_OCTET_STRING *aos = NULL;
230	char *got = NULL;
231	int should_fail = test->want == NULL;
232	int failed = 0;
233
234	if ((aos = s2i_ASN1_OCTET_STRING(NULL, NULL, test->in)) == NULL) {
235		if (!should_fail)
236			errx(1, "%s: s2i_ASN1_OCTET_STRING", test->desc);
237		goto done;
238	}
239
240	if ((got = i2s_ASN1_OCTET_STRING(NULL, aos)) == NULL)
241		errx(1, "%s: i2s_ASN1_OCTET_STRING", test->desc);
242
243	assert(test->want != NULL);
244	if (strcmp(test->want, got) != 0) {
245		fprintf(stderr, "%s: \"%s\" failed: want \"%s\", got \"%s\"\n",
246		    __func__, test->desc, test->want, got);
247		failed |= 1;
248	}
249
250 done:
251	ASN1_OCTET_STRING_free(aos);
252	free(got);
253
254	return failed;
255}
256
257static int
258run_s2i_ASN1_OCTET_STRING_tests(void)
259{
260	size_t i;
261	int failed = 0;
262
263	failed |= test_new_ASN1_OCTET_STRING();
264
265	for (i = 0; i < N_S2I_TESTS; i++)
266		failed |= test_s2i_ASN1_OCTET_STRING(&s2i_test[i]);
267
268	return failed;
269}
270
271int
272main(void)
273{
274	int failed = 0;
275
276	failed |= run_i2s_ASN1_OCTET_STRING_tests();
277	failed |= run_s2i_ASN1_OCTET_STRING_tests();
278
279	return failed;
280}
281