Add ASN1_BIT_STRING_get_length()

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)
This commit is contained in:
Theo Buehler
2025-12-11 08:38:53 -07:00
committed by Neil Horman
parent d78e642f28
commit da8f09846b
8 changed files with 362 additions and 0 deletions

View File

@@ -220,3 +220,36 @@ int ASN1_BIT_STRING_check(const ASN1_BIT_STRING *a,
}
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;
}

View File

@@ -479,6 +479,10 @@ DEPEND[html/man3/ADMISSIONS.html]=man3/ADMISSIONS.pod
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
@@ -3165,6 +3169,7 @@ DEPEND[man/man3/s2i_ASN1_IA5STRING.3]=man3/s2i_ASN1_IA5STRING.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 \
@@ -3837,6 +3842,7 @@ html/man3/i2d_re_X509_tbs.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 \

View File

@@ -0,0 +1,35 @@
=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

View File

@@ -586,6 +586,8 @@ int ASN1_BIT_STRING_name_print(BIO *out, ASN1_BIT_STRING *bs,
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 */
{-

268
test/asn1_string_test.c Normal file
View File

@@ -0,0 +1,268 @@
/*
* 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;
}

View File

@@ -1096,6 +1096,11 @@ IF[{- !$disabled{tests} -}]
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

View File

@@ -0,0 +1,12 @@
#! /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");

View File

@@ -2614,6 +2614,7 @@ ASN1_BIT_STRING_set ? 4_0_0 EXIST::FUNCTION:
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: