1*7ae33d3dStb /* $OpenBSD: asn1api.c,v 1.3 2022/07/09 14:47:42 tb Exp $ */
26163b1efSjsing /*
36163b1efSjsing * Copyright (c) 2021 Joel Sing <jsing@openbsd.org>
46163b1efSjsing *
56163b1efSjsing * Permission to use, copy, modify, and distribute this software for any
66163b1efSjsing * purpose with or without fee is hereby granted, provided that the above
76163b1efSjsing * copyright notice and this permission notice appear in all copies.
86163b1efSjsing *
96163b1efSjsing * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
106163b1efSjsing * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
116163b1efSjsing * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
126163b1efSjsing * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
136163b1efSjsing * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
146163b1efSjsing * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
156163b1efSjsing * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
166163b1efSjsing */
176163b1efSjsing
186163b1efSjsing #include <openssl/asn1.h>
196163b1efSjsing #include <openssl/err.h>
206163b1efSjsing
216163b1efSjsing #include <err.h>
226163b1efSjsing #include <stdio.h>
236163b1efSjsing #include <string.h>
246163b1efSjsing
256163b1efSjsing const long asn1_tag2bits[] = {
266163b1efSjsing [0] = 0,
276163b1efSjsing [1] = 0,
286163b1efSjsing [2] = 0,
296163b1efSjsing [3] = B_ASN1_BIT_STRING,
306163b1efSjsing [4] = B_ASN1_OCTET_STRING,
316163b1efSjsing [5] = 0,
326163b1efSjsing [6] = 0,
336163b1efSjsing [7] = B_ASN1_UNKNOWN,
346163b1efSjsing [8] = B_ASN1_UNKNOWN,
356163b1efSjsing [9] = B_ASN1_UNKNOWN,
366163b1efSjsing [10] = B_ASN1_UNKNOWN,
376163b1efSjsing [11] = B_ASN1_UNKNOWN,
386163b1efSjsing [12] = B_ASN1_UTF8STRING,
396163b1efSjsing [13] = B_ASN1_UNKNOWN,
406163b1efSjsing [14] = B_ASN1_UNKNOWN,
416163b1efSjsing [15] = B_ASN1_UNKNOWN,
426163b1efSjsing [16] = B_ASN1_SEQUENCE,
436163b1efSjsing [17] = 0,
446163b1efSjsing [18] = B_ASN1_NUMERICSTRING,
456163b1efSjsing [19] = B_ASN1_PRINTABLESTRING,
466163b1efSjsing [20] = B_ASN1_T61STRING,
476163b1efSjsing [21] = B_ASN1_VIDEOTEXSTRING,
486163b1efSjsing [22] = B_ASN1_IA5STRING,
496163b1efSjsing [23] = B_ASN1_UTCTIME,
506163b1efSjsing [24] = B_ASN1_GENERALIZEDTIME,
516163b1efSjsing [25] = B_ASN1_GRAPHICSTRING,
526163b1efSjsing [26] = B_ASN1_ISO64STRING,
536163b1efSjsing [27] = B_ASN1_GENERALSTRING,
546163b1efSjsing [28] = B_ASN1_UNIVERSALSTRING,
556163b1efSjsing [29] = B_ASN1_UNKNOWN,
566163b1efSjsing [30] = B_ASN1_BMPSTRING,
576163b1efSjsing };
586163b1efSjsing
596163b1efSjsing static int
asn1_tag2bit(void)606163b1efSjsing asn1_tag2bit(void)
616163b1efSjsing {
626163b1efSjsing int failed = 1;
636163b1efSjsing long bit;
646163b1efSjsing int i;
656163b1efSjsing
666163b1efSjsing for (i = -3; i <= V_ASN1_NEG + 30; i++) {
676163b1efSjsing bit = ASN1_tag2bit(i);
686163b1efSjsing if (i >= 0 && i <= 30) {
696163b1efSjsing if (bit != asn1_tag2bits[i]) {
706163b1efSjsing fprintf(stderr, "FAIL: ASN1_tag2bit(%d) = 0x%lx,"
716163b1efSjsing " want 0x%lx\n", i, bit, asn1_tag2bits[i]);
726163b1efSjsing goto failed;
736163b1efSjsing }
746163b1efSjsing } else {
756163b1efSjsing if (bit != 0) {
766163b1efSjsing fprintf(stderr, "FAIL: ASN1_tag2bit(%d) = 0x%lx,"
776163b1efSjsing " want 0x0\n", i, bit);
786163b1efSjsing goto failed;
796163b1efSjsing }
806163b1efSjsing }
816163b1efSjsing }
826163b1efSjsing
836163b1efSjsing failed = 0;
846163b1efSjsing
856163b1efSjsing failed:
866163b1efSjsing return failed;
876163b1efSjsing }
886163b1efSjsing
896163b1efSjsing static int
asn1_tag2str(void)906163b1efSjsing asn1_tag2str(void)
916163b1efSjsing {
926163b1efSjsing int failed = 1;
936163b1efSjsing const char *s;
946163b1efSjsing int i;
956163b1efSjsing
966163b1efSjsing for (i = -3; i <= V_ASN1_NEG + 30; i++) {
976163b1efSjsing if ((s = ASN1_tag2str(i)) == NULL) {
986163b1efSjsing fprintf(stderr, "FAIL: ASN1_tag2str(%d) returned "
996163b1efSjsing "NULL\n", i);
1006163b1efSjsing goto failed;
1016163b1efSjsing }
1026163b1efSjsing if ((i >= 0 && i <= 30) || i == V_ASN1_NEG_INTEGER ||
1036163b1efSjsing i == V_ASN1_NEG_ENUMERATED) {
1046163b1efSjsing if (strcmp(s, "(unknown)") == 0) {
1056163b1efSjsing fprintf(stderr, "FAIL: ASN1_tag2str(%d) = '%s',"
1066163b1efSjsing " want tag name\n", i, s);
1076163b1efSjsing goto failed;
1086163b1efSjsing }
1096163b1efSjsing } else {
1106163b1efSjsing if (strcmp(s, "(unknown)") != 0) {
1116163b1efSjsing fprintf(stderr, "FAIL: ASN1_tag2str(%d) = '%s',"
1126163b1efSjsing " want '(unknown')\n", i, s);
1136163b1efSjsing goto failed;
1146163b1efSjsing }
1156163b1efSjsing }
1166163b1efSjsing }
1176163b1efSjsing
1186163b1efSjsing failed = 0;
1196163b1efSjsing
1206163b1efSjsing failed:
1216163b1efSjsing return failed;
1226163b1efSjsing }
1236163b1efSjsing
12417ab3ed2Sjsing struct asn1_get_object_test {
12517ab3ed2Sjsing const uint8_t asn1[64];
12617ab3ed2Sjsing size_t asn1_len;
12717ab3ed2Sjsing size_t asn1_hdr_len;
12817ab3ed2Sjsing int want_ret;
12917ab3ed2Sjsing long want_length;
13017ab3ed2Sjsing int want_tag;
13117ab3ed2Sjsing int want_class;
13217ab3ed2Sjsing int want_error;
13317ab3ed2Sjsing };
13417ab3ed2Sjsing
13517ab3ed2Sjsing const struct asn1_get_object_test asn1_get_object_tests[] = {
13617ab3ed2Sjsing {
13717ab3ed2Sjsing /* Zero tag and zero length (EOC). */
13817ab3ed2Sjsing .asn1 = {0x00, 0x00},
13917ab3ed2Sjsing .asn1_len = 2,
14017ab3ed2Sjsing .asn1_hdr_len = 2,
14117ab3ed2Sjsing .want_ret = 0x00,
14217ab3ed2Sjsing .want_length = 0,
14317ab3ed2Sjsing .want_tag = 0,
14417ab3ed2Sjsing .want_class = 0,
14517ab3ed2Sjsing },
14617ab3ed2Sjsing {
14717ab3ed2Sjsing /* Boolean with short form length. */
14817ab3ed2Sjsing .asn1 = {0x01, 0x01},
14917ab3ed2Sjsing .asn1_len = 3,
15017ab3ed2Sjsing .asn1_hdr_len = 2,
15117ab3ed2Sjsing .want_ret = 0x00,
15217ab3ed2Sjsing .want_length = 1,
15317ab3ed2Sjsing .want_tag = 1,
15417ab3ed2Sjsing .want_class = 0,
15517ab3ed2Sjsing },
15617ab3ed2Sjsing {
15717ab3ed2Sjsing /* Long form tag. */
15817ab3ed2Sjsing .asn1 = {0x1f, 0x7f, 0x01},
15917ab3ed2Sjsing .asn1_len = 3 + 128,
16017ab3ed2Sjsing .asn1_hdr_len = 3,
16117ab3ed2Sjsing .want_ret = 0x00,
16217ab3ed2Sjsing .want_length = 1,
16317ab3ed2Sjsing .want_tag = 127,
16417ab3ed2Sjsing .want_class = 0,
16517ab3ed2Sjsing },
16617ab3ed2Sjsing {
16717ab3ed2Sjsing /* Long form tag with class application. */
16817ab3ed2Sjsing .asn1 = {0x5f, 0x7f, 0x01},
16917ab3ed2Sjsing .asn1_len = 3 + 128,
17017ab3ed2Sjsing .asn1_hdr_len = 3,
17117ab3ed2Sjsing .want_ret = 0x00,
17217ab3ed2Sjsing .want_length = 1,
17317ab3ed2Sjsing .want_tag = 127,
17417ab3ed2Sjsing .want_class = 1 << 6,
17517ab3ed2Sjsing },
17617ab3ed2Sjsing {
17717ab3ed2Sjsing /* Long form tag with class context-specific. */
17817ab3ed2Sjsing .asn1 = {0x9f, 0x7f, 0x01},
17917ab3ed2Sjsing .asn1_len = 3 + 128,
18017ab3ed2Sjsing .asn1_hdr_len = 3,
18117ab3ed2Sjsing .want_ret = 0x00,
18217ab3ed2Sjsing .want_length = 1,
18317ab3ed2Sjsing .want_tag = 127,
18417ab3ed2Sjsing .want_class = 2 << 6,
18517ab3ed2Sjsing },
18617ab3ed2Sjsing {
18717ab3ed2Sjsing /* Long form tag with class private. */
18817ab3ed2Sjsing .asn1 = {0xdf, 0x7f, 0x01},
18917ab3ed2Sjsing .asn1_len = 3 + 128,
19017ab3ed2Sjsing .asn1_hdr_len = 3,
19117ab3ed2Sjsing .want_ret = 0x00,
19217ab3ed2Sjsing .want_length = 1,
19317ab3ed2Sjsing .want_tag = 127,
19417ab3ed2Sjsing .want_class = 3 << 6,
19517ab3ed2Sjsing },
19617ab3ed2Sjsing {
19717ab3ed2Sjsing /* Long form tag (maximum). */
19817ab3ed2Sjsing .asn1 = {0x1f, 0x87, 0xff, 0xff, 0xff, 0x7f, 0x01},
19917ab3ed2Sjsing .asn1_len = 8,
20017ab3ed2Sjsing .asn1_hdr_len = 7,
20117ab3ed2Sjsing .want_ret = 0x00,
20217ab3ed2Sjsing .want_length = 1,
20317ab3ed2Sjsing .want_tag = 0x7fffffff,
20417ab3ed2Sjsing .want_class = 0,
20517ab3ed2Sjsing },
20617ab3ed2Sjsing {
20717ab3ed2Sjsing /* Long form tag (maximum + 1). */
20817ab3ed2Sjsing .asn1 = {0x1f, 0x88, 0x80, 0x80, 0x80, 0x00, 0x01},
20917ab3ed2Sjsing .asn1_len = 8,
21017ab3ed2Sjsing .asn1_hdr_len = 7,
21117ab3ed2Sjsing .want_ret = 0x80,
21217ab3ed2Sjsing .want_error = ASN1_R_HEADER_TOO_LONG,
21317ab3ed2Sjsing },
21417ab3ed2Sjsing {
21517ab3ed2Sjsing /* OctetString with long form length. */
21617ab3ed2Sjsing .asn1 = {0x04, 0x81, 0x80},
21717ab3ed2Sjsing .asn1_len = 3 + 128,
21817ab3ed2Sjsing .asn1_hdr_len = 3,
21917ab3ed2Sjsing .want_ret = 0x00,
22017ab3ed2Sjsing .want_length = 128,
22117ab3ed2Sjsing .want_tag = 4,
22217ab3ed2Sjsing .want_class = 0,
22317ab3ed2Sjsing },
22417ab3ed2Sjsing {
22517ab3ed2Sjsing /* OctetString with long form length. */
22617ab3ed2Sjsing .asn1 = {0x04, 0x84, 0x7f, 0xff, 0xff, 0xf9},
22717ab3ed2Sjsing .asn1_len = 0x7fffffff,
22817ab3ed2Sjsing .asn1_hdr_len = 6,
22917ab3ed2Sjsing .want_ret = 0x00,
23017ab3ed2Sjsing .want_length = 0x7ffffff9,
23117ab3ed2Sjsing .want_tag = 4,
23217ab3ed2Sjsing .want_class = 0,
23317ab3ed2Sjsing },
23417ab3ed2Sjsing {
23517ab3ed2Sjsing /* Long form tag and long form length. */
23617ab3ed2Sjsing .asn1 = {0x1f, 0x87, 0xff, 0xff, 0xff, 0x7f, 0x84, 0x7f, 0xff, 0xff, 0xf4},
23717ab3ed2Sjsing .asn1_len = 0x7fffffff,
23817ab3ed2Sjsing .asn1_hdr_len = 11,
23917ab3ed2Sjsing .want_ret = 0x00,
24017ab3ed2Sjsing .want_length = 0x7ffffff4,
24117ab3ed2Sjsing .want_tag = 0x7fffffff,
24217ab3ed2Sjsing .want_class = 0,
24317ab3ed2Sjsing },
24417ab3ed2Sjsing {
24517ab3ed2Sjsing /* Constructed OctetString with definite length. */
24617ab3ed2Sjsing .asn1 = {0x24, 0x03},
24717ab3ed2Sjsing .asn1_len = 5,
24817ab3ed2Sjsing .asn1_hdr_len = 2,
24917ab3ed2Sjsing .want_ret = 0x20,
25017ab3ed2Sjsing .want_length = 3,
25117ab3ed2Sjsing .want_tag = 4,
25217ab3ed2Sjsing .want_class = 0,
25317ab3ed2Sjsing },
25417ab3ed2Sjsing {
25517ab3ed2Sjsing /* Constructed OctetString with indefinite length. */
25617ab3ed2Sjsing .asn1 = {0x24, 0x80},
25717ab3ed2Sjsing .asn1_len = 5,
25817ab3ed2Sjsing .asn1_hdr_len = 2,
25917ab3ed2Sjsing .want_ret = 0x21,
26017ab3ed2Sjsing .want_length = 0,
26117ab3ed2Sjsing .want_tag = 4,
26217ab3ed2Sjsing .want_class = 0,
26317ab3ed2Sjsing },
26417ab3ed2Sjsing {
26517ab3ed2Sjsing /* Boolean with indefinite length (invalid). */
26617ab3ed2Sjsing .asn1 = {0x01, 0x80},
26717ab3ed2Sjsing .asn1_len = 3,
26817ab3ed2Sjsing .want_ret = 0x80,
26917ab3ed2Sjsing .want_error = ASN1_R_HEADER_TOO_LONG,
27017ab3ed2Sjsing },
27117ab3ed2Sjsing {
27217ab3ed2Sjsing /* OctetString with insufficient data (only tag). */
27317ab3ed2Sjsing .asn1 = {0x04, 0x04},
27417ab3ed2Sjsing .asn1_len = 1,
27517ab3ed2Sjsing .want_ret = 0x80,
27617ab3ed2Sjsing .want_error = ASN1_R_HEADER_TOO_LONG,
27717ab3ed2Sjsing },
27817ab3ed2Sjsing {
27917ab3ed2Sjsing /* OctetString with insufficient data (missing content). */
28017ab3ed2Sjsing .asn1 = {0x04, 0x04},
28117ab3ed2Sjsing .asn1_len = 2,
28217ab3ed2Sjsing .asn1_hdr_len = 2,
28317ab3ed2Sjsing .want_ret = 0x80,
28417ab3ed2Sjsing .want_length = 4,
28517ab3ed2Sjsing .want_tag = 4,
28617ab3ed2Sjsing .want_class = 0,
28717ab3ed2Sjsing .want_error = ASN1_R_TOO_LONG,
28817ab3ed2Sjsing },
28917ab3ed2Sjsing {
29017ab3ed2Sjsing /* OctetString with insufficient data (partial content). */
29117ab3ed2Sjsing .asn1 = {0x04, 0x04},
29217ab3ed2Sjsing .asn1_len = 5,
29317ab3ed2Sjsing .asn1_hdr_len = 2,
29417ab3ed2Sjsing .want_ret = 0x80,
29517ab3ed2Sjsing .want_length = 4,
29617ab3ed2Sjsing .want_tag = 4,
29717ab3ed2Sjsing .want_class = 0,
29817ab3ed2Sjsing .want_error = ASN1_R_TOO_LONG,
29917ab3ed2Sjsing },
30017ab3ed2Sjsing {
30117ab3ed2Sjsing /* Constructed OctetString with insufficient data (only tag/len). */
30217ab3ed2Sjsing .asn1 = {0x24, 0x04},
30317ab3ed2Sjsing .asn1_len = 2,
30417ab3ed2Sjsing .asn1_hdr_len = 2,
30517ab3ed2Sjsing .want_ret = 0xa0,
30617ab3ed2Sjsing .want_length = 4,
30717ab3ed2Sjsing .want_tag = 4,
30817ab3ed2Sjsing .want_class = 0,
30917ab3ed2Sjsing .want_error = ASN1_R_TOO_LONG,
31017ab3ed2Sjsing },
31117ab3ed2Sjsing };
31217ab3ed2Sjsing
31317ab3ed2Sjsing #define N_ASN1_GET_OBJECT_TESTS \
31417ab3ed2Sjsing (sizeof(asn1_get_object_tests) / sizeof(*asn1_get_object_tests))
31517ab3ed2Sjsing
31617ab3ed2Sjsing static int
asn1_get_object(void)31717ab3ed2Sjsing asn1_get_object(void)
31817ab3ed2Sjsing {
31917ab3ed2Sjsing const struct asn1_get_object_test *agot;
32017ab3ed2Sjsing const uint8_t *p;
32117ab3ed2Sjsing int ret, tag, tag_class;
32217ab3ed2Sjsing long err, length;
32317ab3ed2Sjsing size_t i;
32417ab3ed2Sjsing int failed = 1;
32517ab3ed2Sjsing
32617ab3ed2Sjsing for (i = 0; i < N_ASN1_GET_OBJECT_TESTS; i++) {
32717ab3ed2Sjsing agot = &asn1_get_object_tests[i];
32817ab3ed2Sjsing
32917ab3ed2Sjsing ERR_clear_error();
33017ab3ed2Sjsing
33117ab3ed2Sjsing p = agot->asn1;
33217ab3ed2Sjsing ret = ASN1_get_object(&p, &length, &tag, &tag_class, agot->asn1_len);
33317ab3ed2Sjsing
33417ab3ed2Sjsing if (ret != agot->want_ret) {
33517ab3ed2Sjsing fprintf(stderr, "FAIL: %zu - got return value %x, want %x\n",
33617ab3ed2Sjsing i, ret, agot->want_ret);
33717ab3ed2Sjsing goto failed;
33817ab3ed2Sjsing }
33917ab3ed2Sjsing if (ret & 0x80) {
34017ab3ed2Sjsing err = ERR_peek_error();
34117ab3ed2Sjsing if (ERR_GET_REASON(err) != agot->want_error) {
34217ab3ed2Sjsing fprintf(stderr, "FAIL: %zu - got error reason %d, "
34317ab3ed2Sjsing "want %d\n", i, ERR_GET_REASON(err),
34417ab3ed2Sjsing agot->want_error);
34517ab3ed2Sjsing goto failed;
34617ab3ed2Sjsing }
34717ab3ed2Sjsing if (ERR_GET_REASON(err) == ASN1_R_HEADER_TOO_LONG) {
34817ab3ed2Sjsing if (p != agot->asn1) {
34917ab3ed2Sjsing fprintf(stderr, "FAIL: %zu - got ber_in %p, "
35017ab3ed2Sjsing "want %p\n", i, p, agot->asn1);
35117ab3ed2Sjsing goto failed;
35217ab3ed2Sjsing }
35317ab3ed2Sjsing continue;
35417ab3ed2Sjsing }
35517ab3ed2Sjsing }
35617ab3ed2Sjsing if (length != agot->want_length) {
35717ab3ed2Sjsing fprintf(stderr, "FAIL: %zu - got length %ld, want %ld\n",
35817ab3ed2Sjsing i, length, agot->want_length);
35917ab3ed2Sjsing goto failed;
36017ab3ed2Sjsing }
36117ab3ed2Sjsing if (tag != agot->want_tag) {
36217ab3ed2Sjsing fprintf(stderr, "FAIL: %zu - got tag %d, want %d\n",
36317ab3ed2Sjsing i, tag, agot->want_tag);
36417ab3ed2Sjsing goto failed;
36517ab3ed2Sjsing }
36617ab3ed2Sjsing if (tag_class != agot->want_class) {
36717ab3ed2Sjsing fprintf(stderr, "FAIL: %zu - got class %d, want %d\n",
36817ab3ed2Sjsing i, tag_class, agot->want_class);
36917ab3ed2Sjsing goto failed;
37017ab3ed2Sjsing }
37117ab3ed2Sjsing if (p != agot->asn1 + agot->asn1_hdr_len) {
37217ab3ed2Sjsing fprintf(stderr, "FAIL: %zu - got ber_in %p, want %p\n",
37317ab3ed2Sjsing i, p, agot->asn1 + agot->asn1_len);
37417ab3ed2Sjsing goto failed;
37517ab3ed2Sjsing }
37617ab3ed2Sjsing }
37717ab3ed2Sjsing
37817ab3ed2Sjsing failed = 0;
37917ab3ed2Sjsing
38017ab3ed2Sjsing failed:
38117ab3ed2Sjsing return failed;
38217ab3ed2Sjsing }
38317ab3ed2Sjsing
384*7ae33d3dStb static int
asn1_integer_get_null_test(void)385*7ae33d3dStb asn1_integer_get_null_test(void)
386*7ae33d3dStb {
387*7ae33d3dStb int failed = 0;
388*7ae33d3dStb long ret;
389*7ae33d3dStb
390*7ae33d3dStb if ((ret = ASN1_INTEGER_get(NULL)) != 0) {
391*7ae33d3dStb fprintf(stderr, "FAIL: ASN1_INTEGER_get(NULL) %ld != 0\n", ret);
392*7ae33d3dStb failed |= 1;
393*7ae33d3dStb }
394*7ae33d3dStb
395*7ae33d3dStb if ((ret = ASN1_ENUMERATED_get(NULL)) != 0) {
396*7ae33d3dStb fprintf(stderr, "FAIL: ASN1_ENUMERATED_get(NULL) %ld != 0\n",
397*7ae33d3dStb ret);
398*7ae33d3dStb failed |= 1;
399*7ae33d3dStb }
400*7ae33d3dStb
401*7ae33d3dStb return failed;
402*7ae33d3dStb }
403*7ae33d3dStb
4046163b1efSjsing int
main(int argc,char ** argv)4056163b1efSjsing main(int argc, char **argv)
4066163b1efSjsing {
4076163b1efSjsing int failed = 0;
4086163b1efSjsing
4096163b1efSjsing failed |= asn1_tag2bit();
4106163b1efSjsing failed |= asn1_tag2str();
41117ab3ed2Sjsing failed |= asn1_get_object();
412*7ae33d3dStb failed |= asn1_integer_get_null_test();
4136163b1efSjsing
4146163b1efSjsing return (failed);
4156163b1efSjsing }
416