-/* $OpenBSD: asn1api.c,v 1.1 2021/12/14 17:07:57 jsing Exp $ */
+/* $OpenBSD: asn1api.c,v 1.2 2021/12/14 17:10:02 jsing Exp $ */
/*
* Copyright (c) 2021 Joel Sing <jsing@openbsd.org>
*
return failed;
}
+struct asn1_get_object_test {
+ const uint8_t asn1[64];
+ size_t asn1_len;
+ size_t asn1_hdr_len;
+ int want_ret;
+ long want_length;
+ int want_tag;
+ int want_class;
+ int want_error;
+};
+
+const struct asn1_get_object_test asn1_get_object_tests[] = {
+ {
+ /* Zero tag and zero length (EOC). */
+ .asn1 = {0x00, 0x00},
+ .asn1_len = 2,
+ .asn1_hdr_len = 2,
+ .want_ret = 0x00,
+ .want_length = 0,
+ .want_tag = 0,
+ .want_class = 0,
+ },
+ {
+ /* Boolean with short form length. */
+ .asn1 = {0x01, 0x01},
+ .asn1_len = 3,
+ .asn1_hdr_len = 2,
+ .want_ret = 0x00,
+ .want_length = 1,
+ .want_tag = 1,
+ .want_class = 0,
+ },
+ {
+ /* Long form tag. */
+ .asn1 = {0x1f, 0x7f, 0x01},
+ .asn1_len = 3 + 128,
+ .asn1_hdr_len = 3,
+ .want_ret = 0x00,
+ .want_length = 1,
+ .want_tag = 127,
+ .want_class = 0,
+ },
+ {
+ /* Long form tag with class application. */
+ .asn1 = {0x5f, 0x7f, 0x01},
+ .asn1_len = 3 + 128,
+ .asn1_hdr_len = 3,
+ .want_ret = 0x00,
+ .want_length = 1,
+ .want_tag = 127,
+ .want_class = 1 << 6,
+ },
+ {
+ /* Long form tag with class context-specific. */
+ .asn1 = {0x9f, 0x7f, 0x01},
+ .asn1_len = 3 + 128,
+ .asn1_hdr_len = 3,
+ .want_ret = 0x00,
+ .want_length = 1,
+ .want_tag = 127,
+ .want_class = 2 << 6,
+ },
+ {
+ /* Long form tag with class private. */
+ .asn1 = {0xdf, 0x7f, 0x01},
+ .asn1_len = 3 + 128,
+ .asn1_hdr_len = 3,
+ .want_ret = 0x00,
+ .want_length = 1,
+ .want_tag = 127,
+ .want_class = 3 << 6,
+ },
+ {
+ /* Long form tag (maximum). */
+ .asn1 = {0x1f, 0x87, 0xff, 0xff, 0xff, 0x7f, 0x01},
+ .asn1_len = 8,
+ .asn1_hdr_len = 7,
+ .want_ret = 0x00,
+ .want_length = 1,
+ .want_tag = 0x7fffffff,
+ .want_class = 0,
+ },
+ {
+ /* Long form tag (maximum + 1). */
+ .asn1 = {0x1f, 0x88, 0x80, 0x80, 0x80, 0x00, 0x01},
+ .asn1_len = 8,
+ .asn1_hdr_len = 7,
+ .want_ret = 0x80,
+ .want_error = ASN1_R_HEADER_TOO_LONG,
+ },
+ {
+ /* OctetString with long form length. */
+ .asn1 = {0x04, 0x81, 0x80},
+ .asn1_len = 3 + 128,
+ .asn1_hdr_len = 3,
+ .want_ret = 0x00,
+ .want_length = 128,
+ .want_tag = 4,
+ .want_class = 0,
+ },
+ {
+ /* OctetString with long form length. */
+ .asn1 = {0x04, 0x84, 0x7f, 0xff, 0xff, 0xf9},
+ .asn1_len = 0x7fffffff,
+ .asn1_hdr_len = 6,
+ .want_ret = 0x00,
+ .want_length = 0x7ffffff9,
+ .want_tag = 4,
+ .want_class = 0,
+ },
+ {
+ /* Long form tag and long form length. */
+ .asn1 = {0x1f, 0x87, 0xff, 0xff, 0xff, 0x7f, 0x84, 0x7f, 0xff, 0xff, 0xf4},
+ .asn1_len = 0x7fffffff,
+ .asn1_hdr_len = 11,
+ .want_ret = 0x00,
+ .want_length = 0x7ffffff4,
+ .want_tag = 0x7fffffff,
+ .want_class = 0,
+ },
+ {
+ /* Constructed OctetString with definite length. */
+ .asn1 = {0x24, 0x03},
+ .asn1_len = 5,
+ .asn1_hdr_len = 2,
+ .want_ret = 0x20,
+ .want_length = 3,
+ .want_tag = 4,
+ .want_class = 0,
+ },
+ {
+ /* Constructed OctetString with indefinite length. */
+ .asn1 = {0x24, 0x80},
+ .asn1_len = 5,
+ .asn1_hdr_len = 2,
+ .want_ret = 0x21,
+ .want_length = 0,
+ .want_tag = 4,
+ .want_class = 0,
+ },
+ {
+ /* Boolean with indefinite length (invalid). */
+ .asn1 = {0x01, 0x80},
+ .asn1_len = 3,
+ .want_ret = 0x80,
+ .want_error = ASN1_R_HEADER_TOO_LONG,
+ },
+ {
+ /* OctetString with insufficient data (only tag). */
+ .asn1 = {0x04, 0x04},
+ .asn1_len = 1,
+ .want_ret = 0x80,
+ .want_error = ASN1_R_HEADER_TOO_LONG,
+ },
+ {
+ /* OctetString with insufficient data (missing content). */
+ .asn1 = {0x04, 0x04},
+ .asn1_len = 2,
+ .asn1_hdr_len = 2,
+ .want_ret = 0x80,
+ .want_length = 4,
+ .want_tag = 4,
+ .want_class = 0,
+ .want_error = ASN1_R_TOO_LONG,
+ },
+ {
+ /* OctetString with insufficient data (partial content). */
+ .asn1 = {0x04, 0x04},
+ .asn1_len = 5,
+ .asn1_hdr_len = 2,
+ .want_ret = 0x80,
+ .want_length = 4,
+ .want_tag = 4,
+ .want_class = 0,
+ .want_error = ASN1_R_TOO_LONG,
+ },
+ {
+ /* Constructed OctetString with insufficient data (only tag/len). */
+ .asn1 = {0x24, 0x04},
+ .asn1_len = 2,
+ .asn1_hdr_len = 2,
+ .want_ret = 0xa0,
+ .want_length = 4,
+ .want_tag = 4,
+ .want_class = 0,
+ .want_error = ASN1_R_TOO_LONG,
+ },
+};
+
+#define N_ASN1_GET_OBJECT_TESTS \
+ (sizeof(asn1_get_object_tests) / sizeof(*asn1_get_object_tests))
+
+static int
+asn1_get_object(void)
+{
+ const struct asn1_get_object_test *agot;
+ const uint8_t *p;
+ int ret, tag, tag_class;
+ long err, length;
+ size_t i;
+ int failed = 1;
+
+ for (i = 0; i < N_ASN1_GET_OBJECT_TESTS; i++) {
+ agot = &asn1_get_object_tests[i];
+
+ ERR_clear_error();
+
+ p = agot->asn1;
+ ret = ASN1_get_object(&p, &length, &tag, &tag_class, agot->asn1_len);
+
+ if (ret != agot->want_ret) {
+ fprintf(stderr, "FAIL: %zu - got return value %x, want %x\n",
+ i, ret, agot->want_ret);
+ goto failed;
+ }
+ if (ret & 0x80) {
+ err = ERR_peek_error();
+ if (ERR_GET_REASON(err) != agot->want_error) {
+ fprintf(stderr, "FAIL: %zu - got error reason %d, "
+ "want %d\n", i, ERR_GET_REASON(err),
+ agot->want_error);
+ goto failed;
+ }
+ if (ERR_GET_REASON(err) == ASN1_R_HEADER_TOO_LONG) {
+ if (p != agot->asn1) {
+ fprintf(stderr, "FAIL: %zu - got ber_in %p, "
+ "want %p\n", i, p, agot->asn1);
+ goto failed;
+ }
+ continue;
+ }
+ }
+ if (length != agot->want_length) {
+ fprintf(stderr, "FAIL: %zu - got length %ld, want %ld\n",
+ i, length, agot->want_length);
+ goto failed;
+ }
+ if (tag != agot->want_tag) {
+ fprintf(stderr, "FAIL: %zu - got tag %d, want %d\n",
+ i, tag, agot->want_tag);
+ goto failed;
+ }
+ if (tag_class != agot->want_class) {
+ fprintf(stderr, "FAIL: %zu - got class %d, want %d\n",
+ i, tag_class, agot->want_class);
+ goto failed;
+ }
+ if (p != agot->asn1 + agot->asn1_hdr_len) {
+ fprintf(stderr, "FAIL: %zu - got ber_in %p, want %p\n",
+ i, p, agot->asn1 + agot->asn1_len);
+ goto failed;
+ }
+ }
+
+ failed = 0;
+
+ failed:
+ return failed;
+}
+
int
main(int argc, char **argv)
{
failed |= asn1_tag2bit();
failed |= asn1_tag2str();
+ failed |= asn1_get_object();
return (failed);
}