From tb@openbsd.org with tests adapted by beck for OpenSSL.
Fixes: https://github.com/openssl/openssl/issues/29184
Reviewed-by: Saša Nedvědický <sashan@openssl.org>
Reviewed-by: Tomas Mraz <tomas@openssl.org>
Reviewed-by: Neil Horman <nhorman@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/29387)
}
return ok;
}
+
+int ASN1_BIT_STRING_get_length(const ASN1_BIT_STRING *abs, size_t *out_length,
+ int *out_unused_bits)
+{
+ size_t length;
+ int unused_bits;
+
+ if (abs == NULL || abs->type != V_ASN1_BIT_STRING)
+ return 0;
+
+ if (out_length == NULL || out_unused_bits == NULL)
+ return 0;
+
+ length = abs->length;
+ unused_bits = 0;
+
+ if ((abs->flags & ASN1_STRING_FLAG_BITS_LEFT) != 0)
+ unused_bits = abs->flags & 0x07;
+
+ if (length == 0 && unused_bits != 0)
+ return 0;
+
+ if (unused_bits != 0) {
+ unsigned char mask = (1 << unused_bits) - 1;
+ if ((abs->data[length - 1] & mask) != 0)
+ return 0;
+ }
+
+ *out_length = length;
+ *out_unused_bits = unused_bits;
+
+ return 1;
+}
GENERATE[html/man3/ADMISSIONS.html]=man3/ADMISSIONS.pod
DEPEND[man/man3/ADMISSIONS.3]=man3/ADMISSIONS.pod
GENERATE[man/man3/ADMISSIONS.3]=man3/ADMISSIONS.pod
+DEPEND[html/man3/ASN1_BIT_STRING_get_length.html]=man3/ASN1_BIT_STRING_get_length.pod
+GENERATE[html/man3/ASN1_BIT_STRING_get_length.html]=man3/ASN1_BIT_STRING_get_length.pod
+DEPEND[man/man3/ASN1_BIT_STRING_get_length.3]=man3/ASN1_BIT_STRING_get_length.pod
+GENERATE[man/man3/ASN1_BIT_STRING_get_length.3]=man3/ASN1_BIT_STRING_get_length.pod
DEPEND[html/man3/ASN1_EXTERN_FUNCS.html]=man3/ASN1_EXTERN_FUNCS.pod
GENERATE[html/man3/ASN1_EXTERN_FUNCS.html]=man3/ASN1_EXTERN_FUNCS.pod
DEPEND[man/man3/ASN1_EXTERN_FUNCS.3]=man3/ASN1_EXTERN_FUNCS.pod
GENERATE[man/man3/s2i_ASN1_IA5STRING.3]=man3/s2i_ASN1_IA5STRING.pod
IMAGEDOCS[man3]=
HTMLDOCS[man3]=html/man3/ADMISSIONS.html \
+html/man3/ASN1_BIT_STRING_get_length.html \
html/man3/ASN1_EXTERN_FUNCS.html \
html/man3/ASN1_INTEGER_get_int64.html \
html/man3/ASN1_INTEGER_new.html \
html/man3/o2i_SCT_LIST.html \
html/man3/s2i_ASN1_IA5STRING.html
MANDOCS[man3]=man/man3/ADMISSIONS.3 \
+man/man3/ASN1_BIT_STRING_get_length.3 \
man/man3/ASN1_EXTERN_FUNCS.3 \
man/man3/ASN1_INTEGER_get_int64.3 \
man/man3/ASN1_INTEGER_new.3 \
--- /dev/null
+=pod
+
+=head1 NAME
+
+ASN1_BIT_STRING_get_length - ASN1_BIT_STRING accessors
+
+=head1 SYNOPSIS
+
+ #include <openssl/asn1.h>
+
+ int ASN1_BIT_STRING_get_length(const ASN1_BIT_STRING *bitstr, size_t *length, int *unused_bits);
+
+=head1 DESCRIPTION
+
+ASN1_BIT_STRING_get_length() returns the number of octets in I<bitstr>
+containing bit values in I<length> and the number of unused bits in
+the last octet in I<unused_bits>. The value returned in
+I<unused_bits> is guaranteed to be between 0 and 7, inclusive.
+
+=head1 RETURN VALUES
+
+ASN1_BIT_STRING_get_length() returns 1 on success or 0 if the encoding
+of I<bitstr> is internally inconsistent, or if one of I<bitstr>,
+I<length>, or I<unused_bits> is NULL.
+
+=head1 COPYRIGHT
+
+Copyright 2025 The OpenSSL Project Authors. All Rights Reserved.
+
+Licensed under the Apache License 2.0 (the "License"). You may not use
+this file except in compliance with the License. You can obtain a copy
+in the file LICENSE in the source distribution or at
+L<https://www.openssl.org/source/license.html>.
+
+=cut
int ASN1_BIT_STRING_num_asc(const char *name, BIT_STRING_BITNAME *tbl);
int ASN1_BIT_STRING_set_asc(ASN1_BIT_STRING *bs, const char *name, int value,
BIT_STRING_BITNAME *tbl);
+int ASN1_BIT_STRING_get_length(const ASN1_BIT_STRING *abs, size_t *length,
+ int *unused_bits);
/* clang-format off */
{-
--- /dev/null
+/*
+ * Copyright 2025 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/* ASN1_STRING tests */
+
+#include <stdio.h>
+
+#include <openssl/asn1.h>
+#include "testutil.h"
+
+struct abs_get_length_test {
+ const char *descr;
+ int valid;
+ const unsigned char der[20];
+ int der_len;
+ size_t length;
+ int unused_bits;
+};
+
+static const struct abs_get_length_test abs_get_length_tests[] = {
+ {
+ .descr = "zero bits",
+ .valid = 1,
+ .der = {
+ 0x03,
+ 0x01,
+ 0x00,
+ },
+ .der_len = 3,
+ .length = 0,
+ .unused_bits = 0,
+ },
+ {
+ .descr = "zero bits one unused",
+ .valid = 0,
+ .der = {
+ 0x03,
+ 0x01,
+ 0x01,
+ },
+ .der_len = 3,
+ },
+ {
+ .descr = "single zero bit",
+ .valid = 1,
+ .der = {
+ 0x03,
+ 0x02,
+ 0x07,
+ 0x00,
+ },
+ .der_len = 4,
+ .length = 1,
+ .unused_bits = 7,
+ },
+ {
+ .descr = "single one bit",
+ .valid = 1,
+ .der = {
+ 0x03,
+ 0x02,
+ 0x07,
+ 0x80,
+ },
+ .der_len = 4,
+ .length = 1,
+ .unused_bits = 7,
+ },
+ {
+ /* XXX - the library pretends this is 03 02 07 80 */
+ .descr = "invalid: single one bit, seventh bit set",
+ .valid = 1,
+ .der = {
+ 0x03,
+ 0x02,
+ 0x07,
+ 0xc0,
+ },
+ .der_len = 4,
+ .length = 1,
+ .unused_bits = 7,
+ },
+ {
+ .descr = "x.690, primitive encoding in example 8.6.4.2",
+ .valid = 1,
+ .der = {
+ 0x03,
+ 0x07,
+ 0x04,
+ 0x0A,
+ 0x3b,
+ 0x5F,
+ 0x29,
+ 0x1c,
+ 0xd0,
+ },
+ .der_len = 9,
+ .length = 6,
+ .unused_bits = 4,
+ },
+ {
+ /*
+ * XXX - the library thinks it "decodes" this but gets it
+ * quite wrong. Looks like it uses the unused bits of the
+ * first component, and the unused bits octet 04 of the
+ * second component somehow becomes part of the value.
+ */
+ .descr = "x.690, constructed encoding in example 8.6.4.2",
+ .valid = 1,
+ .der = {
+ 0x23,
+ 0x80,
+ 0x03,
+ 0x03,
+ 0x00,
+ 0x0A,
+ 0x3b,
+ 0x03,
+ 0x05,
+ 0x04,
+ 0x5F,
+ 0x29,
+ 0x1c,
+ 0xd0,
+ 0x00,
+ 0x00,
+ },
+ .der_len = 16,
+ .length = 7, /* XXX - should be 6. */
+ .unused_bits = 0, /* XXX - should be 4. */
+ },
+ {
+ .descr = "RFC 3779, 2.1.1, IPv4 address 10.5.0.4",
+ .valid = 1,
+ .der = {
+ 0x03,
+ 0x05,
+ 0x00,
+ 0x0a,
+ 0x05,
+ 0x00,
+ 0x04,
+ },
+ .der_len = 7,
+ .length = 4,
+ .unused_bits = 0,
+ },
+ {
+ .descr = "RFC 3779, 2.1.1, IPv4 prefix 10.5.0/23",
+ .valid = 1,
+ .der = {
+ 0x03,
+ 0x04,
+ 0x01,
+ 0x0a,
+ 0x05,
+ 0x00,
+ },
+ .der_len = 6,
+ .length = 3,
+ .unused_bits = 1,
+ },
+ {
+ .descr = "RFC 3779, 2.1.1, IPv6 address 2001:0:200:3::1",
+ .valid = 1,
+ .der = {
+ 0x03,
+ 0x11,
+ 0x00,
+ 0x20,
+ 0x01,
+ 0x00,
+ 0x00,
+ 0x02,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x03,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x01,
+ },
+ .der_len = 19,
+ .length = 16,
+ .unused_bits = 0,
+ },
+ {
+ .descr = "RFC 3779, 2.1.1, IPv6 prefix 2001:0:200/39",
+ .valid = 1,
+ .der = {
+ 0x03,
+ 0x06,
+ 0x01,
+ 0x20,
+ 0x01,
+ 0x00,
+ 0x00,
+ 0x02,
+ },
+ .der_len = 8,
+ .length = 5,
+ .unused_bits = 1,
+ },
+};
+
+static int
+abs_get_length_test(const struct abs_get_length_test *tbl, int idx)
+{
+ const struct abs_get_length_test *test = &tbl[idx];
+ ASN1_BIT_STRING *abs = NULL;
+ const unsigned char *p;
+ int unused_bits, ret;
+ size_t length;
+ int success = 0;
+
+ p = test->der;
+ if (!TEST_ptr(abs = d2i_ASN1_BIT_STRING(NULL, &p, test->der_len))) {
+ TEST_info("%s, (idx=%d) - d2i_ASN1_BIT_STRING faled", __func__, idx);
+ goto err;
+ }
+
+ ret = ASN1_BIT_STRING_get_length(abs, &length, &unused_bits);
+ if (!TEST_int_eq(test->valid, ret)) {
+ TEST_info("%s (idx=%d): %s ASN1_BIT_STRING_get_length want %d, got %d\n",
+ __func__, idx, test->descr, test->valid, ret);
+ goto err;
+ }
+ if (!test->valid)
+ goto done;
+
+ if (!TEST_size_t_eq(length, test->length)
+ || !TEST_int_eq(unused_bits, test->unused_bits)) {
+ TEST_info("%s: (idx=%d) %s: want (%zu, %d), got (%zu, %d)\n", __func__,
+ idx, test->descr, test->length, test->unused_bits, length,
+ unused_bits);
+ goto err;
+ }
+
+done:
+ success = 1;
+
+err:
+ ASN1_STRING_free(abs);
+
+ return success;
+}
+
+static int
+asn1_bit_string_get_length_test(int idx)
+{
+ return abs_get_length_test(abs_get_length_tests, idx);
+}
+
+int setup_tests(void)
+{
+ ADD_ALL_TESTS(asn1_bit_string_get_length_test, OSSL_NELEM(abs_get_length_tests));
+ return 1;
+}
INCLUDE[asn1_time_test]=../include ../apps/include
DEPEND[asn1_time_test]=../libcrypto libtestutil.a
+ PROGRAMS{noinst}=asn1_string_test
+ SOURCE[asn1_string_test]=asn1_string_test.c
+ INCLUDE[asn1_string_test]=../include ../apps/include
+ DEPEND[asn1_string_test]=../libcrypto libtestutil.a
+
# We disable this test completely in a shared build because it deliberately
# redefines some internal libssl symbols. This doesn't work in a non-shared
# build
--- /dev/null
+#! /usr/bin/env perl
+# Copyright 2025 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the Apache License 2.0 (the "License"). You may not use
+# this file except in compliance with the License. You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+
+use OpenSSL::Test::Simple;
+
+simple_test("test_asn1_string", "asn1_string_test");
ASN1_BIT_STRING_set_bit ? 4_0_0 EXIST::FUNCTION:
ASN1_BIT_STRING_get_bit ? 4_0_0 EXIST::FUNCTION:
ASN1_BIT_STRING_check ? 4_0_0 EXIST::FUNCTION:
+ASN1_BIT_STRING_get_length ? 4_0_0 EXIST::FUNCTION:
ASN1_BIT_STRING_name_print ? 4_0_0 EXIST::FUNCTION:
ASN1_BIT_STRING_num_asc ? 4_0_0 EXIST::FUNCTION:
ASN1_BIT_STRING_set_asc ? 4_0_0 EXIST::FUNCTION: