Provide ASN1_BIT_STRING_set1()

Mostly work by @botovq with tests adapted to openssl by
@bob-beck

Fixes: https://github.com/openssl/openssl/issues/29185

Reviewed-by: Neil Horman <nhorman@openssl.org>
Reviewed-by: Tomas Mraz <tomas@openssl.org>
MergeDate: Thu Feb 12 20:41:13 2026
(Merged from https://github.com/openssl/openssl/pull/29926)
This commit is contained in:
Theo Buehler
2026-02-02 15:55:32 -07:00
committed by Neil Horman
parent 2ea6e785f5
commit 74d47c8e66
6 changed files with 474 additions and 0 deletions
+5
View File
@@ -125,6 +125,11 @@ OpenSSL 4.0
*Beat Bolli*
* Added ASN1_BIT_STRING_set1() to set a bit string to a value including
the length in bytes and the number of unused bits.
* Bob Beck *
* The deprecated function `ASN1_STRING_data` has been removed.
*Bob Beck*
+49
View File
@@ -13,6 +13,27 @@
#include <openssl/asn1.h>
#include "asn1_local.h"
#include <crypto/asn1.h>
static void
asn1_bit_string_clear_unused_bits(ASN1_BIT_STRING *abs)
{
abs->flags &= ~(ASN1_STRING_FLAG_BITS_LEFT | 0x07);
}
static int asn1_bit_string_set_unused_bits(ASN1_BIT_STRING *abs,
uint8_t unused_bits)
{
if (unused_bits > 7)
return 0;
asn1_bit_string_clear_unused_bits(abs);
abs->flags |= ASN1_STRING_FLAG_BITS_LEFT | unused_bits;
return 1;
}
int ASN1_BIT_STRING_set(ASN1_BIT_STRING *x, unsigned char *d, int len)
{
return ASN1_STRING_set(x, d, len);
@@ -253,3 +274,31 @@ int ASN1_BIT_STRING_get_length(const ASN1_BIT_STRING *abs, size_t *out_length,
return 1;
}
int ASN1_BIT_STRING_set1(ASN1_BIT_STRING *abs, const uint8_t *data, size_t length,
int unused_bits)
{
if (abs == NULL)
return 0;
if (length > INT_MAX || unused_bits < 0 || unused_bits > 7)
return 0;
if (length == 0 && unused_bits != 0)
return 0;
if (length > 0 && (data[length - 1] & ((1 << unused_bits) - 1)) != 0)
return 0;
/*
* XXX - ASN1_STRING_set() and asn1_bit_string_set_unused_bits() preserve the
* state of flags irrelevant to ASN1_BIT_STRING. Should we explicitly
* clear them?
*/
if (!ASN1_STRING_set(abs, data, (int)length))
return 0;
abs->type = V_ASN1_BIT_STRING;
return asn1_bit_string_set_unused_bits(abs, unused_bits);
}
+18
View File
@@ -2,6 +2,7 @@
=head1 NAME
ASN1_BIT_STRING_set1,
ASN1_BIT_STRING_get_length - ASN1_BIT_STRING accessors
=head1 SYNOPSIS
@@ -9,20 +10,37 @@ ASN1_BIT_STRING_get_length - ASN1_BIT_STRING accessors
#include <openssl/asn1.h>
int ASN1_BIT_STRING_get_length(const ASN1_BIT_STRING *bitstr, size_t *length, int *unused_bits);
int ASN1_BIT_STRING_set1(ASN1_BIT_STRING *bitstr, const uint8_t *data, size_t length, int unused_bits);
=head1 DESCRIPTION
The ASN.1 BIT STRING type holds a bit string of arbitrary bit length.
In the distinguished encoding rules DER, its bits are encoded in
groups of eight, leaving between zero and seven bits of the
last octet unused. If there are unused bits, they must all be set to zero.
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.
ASN1_BIT_STRING_set1() sets the type of I<bitstr> to
I<V_ASN1_BIT_STRING> and its octets to the bits in the byte string
I<data> of length I<length> octets, making sure that the last
I<unused_bits> bits in the last byte are zero.
=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.
ASN1_BIT_STRING_set1() returns 1 on success or 0 if memory allocation
fails or if I<bitstr> is NULL , I<length> is too large, I<length>is
zero and I<unused_bits> is nonzero, I<unused_bits> is less than 0 or
greater than 7, or any unused bit in the last octet of I<data> is
nonzero.
=head1 COPYRIGHT
Copyright 2025 The OpenSSL Project Authors. All Rights Reserved.
+2
View File
@@ -586,6 +586,8 @@ 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);
int ASN1_BIT_STRING_set1(ASN1_BIT_STRING *abs, const uint8_t *data,
size_t length, int unused_bits);
/* clang-format off */
{-
+399
View File
@@ -261,8 +261,407 @@ asn1_bit_string_get_length_test(int idx)
return abs_get_length_test(abs_get_length_tests, idx);
}
struct abs_set1_test {
const char *descr;
int valid;
const uint8_t data[20];
size_t length;
int unused_bits;
const unsigned char der[20];
int der_len;
};
static const struct abs_set1_test abs_set1_tests[] = {
{
.descr = "length too large",
.valid = 0,
.length = (size_t)INT_MAX + 1,
},
{
.descr = "negative unused bits",
.valid = 0,
.unused_bits = -1,
},
{
.descr = "8 unused bits",
.valid = 0,
.unused_bits = 8,
},
{
.descr = "empty with unused bits",
.valid = 0,
.data = {
0x00,
},
.length = 0,
.unused_bits = 1,
},
{
.descr = "empty",
.valid = 1,
.data = {
0x00,
},
.length = 0,
.unused_bits = 0,
.der = {
0x03,
0x01,
0x00,
},
.der_len = 3,
},
{
.descr = "single zero bit",
.valid = 1,
.data = {
0x00,
},
.length = 1,
.unused_bits = 7,
.der = {
0x03,
0x02,
0x07,
0x00,
},
.der_len = 4,
},
{
.descr = "single zero bit, with non-zero unused bit 6",
.valid = 0,
.data = {
0x40,
},
.length = 1,
.unused_bits = 7,
},
{
.descr = "single zero bit, with non-zero unused bit 0",
.valid = 0,
.data = {
0x01,
},
.length = 1,
.unused_bits = 7,
},
{
.descr = "single one bit",
.valid = 1,
.data = {
0x80,
},
.length = 1,
.unused_bits = 7,
.der = {
0x03,
0x02,
0x07,
0x80,
},
.der_len = 4,
},
{
.descr = "single one bit, with non-zero unused-bit 6",
.valid = 0,
.data = {
0xc0,
},
.length = 1,
.unused_bits = 7,
},
{
.descr = "single one bit, with non-zero unused-bit 0",
.valid = 0,
.data = {
0x81,
},
.length = 1,
.unused_bits = 7,
},
{
.descr = "RFC 3779, 2.1.1, IPv4 address 10.5.0.4",
.valid = 1,
.data = {
0x0a,
0x05,
0x00,
0x04,
},
.length = 4,
.unused_bits = 0,
.der = {
0x03,
0x05,
0x00,
0x0a,
0x05,
0x00,
0x04,
},
.der_len = 7,
},
{
.descr = "RFC 3779, 2.1.1, IPv4 address 10.5.0/23",
.valid = 1,
.data = {
0x0a,
0x05,
0x00,
},
.length = 3,
.unused_bits = 1,
.der = {
0x03,
0x04,
0x01,
0x0a,
0x05,
0x00,
},
.der_len = 6,
},
{
.descr = "RFC 3779, 2.1.1, IPv4 address 10.5.0/23, unused bit",
.valid = 0,
.data = {
0x0a,
0x05,
0x01,
},
.length = 3,
.unused_bits = 1,
},
{
.descr = "RFC 3779, IPv4 address 10.5.0/17",
.valid = 1,
.data = {
0x0a,
0x05,
0x00,
},
.length = 3,
.unused_bits = 7,
.der = {
0x03,
0x04,
0x07,
0x0a,
0x05,
0x00,
},
.der_len = 6,
},
{
.descr = "RFC 3779, IPv4 address 10.5.0/18, unused bit set",
.valid = 0,
.data = {
0x0a,
0x05,
0x20,
},
.length = 3,
.unused_bits = 6,
},
{
.descr = "RFC 3779, 2.1.1, IPv6 address 2001:0:200:3::1",
.valid = 1,
.data = {
0x20,
0x01,
0x00,
0x00,
0x02,
0x00,
0x00,
0x00,
0x00,
0x03,
0x00,
0x00,
0x00,
0x00,
0x00,
0x01,
},
.length = 16,
.unused_bits = 0,
.der = {
0x03,
0x11,
0x00,
0x20,
0x01,
0x00,
0x00,
0x02,
0x00,
0x00,
0x00,
0x00,
0x03,
0x00,
0x00,
0x00,
0x00,
0x00,
0x01,
},
.der_len = 19,
},
{
.descr = "RFC 3779, IPv6 address 2001:0:200:3::/127",
.valid = 1,
.data = {
0x20,
0x01,
0x00,
0x00,
0x02,
0x00,
0x00,
0x00,
0x00,
0x03,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
},
.length = 16,
.unused_bits = 1,
.der = {
0x03,
0x11,
0x01,
0x20,
0x01,
0x00,
0x00,
0x02,
0x00,
0x00,
0x00,
0x00,
0x03,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
},
.der_len = 19,
},
{
.descr = "RFC 3779, IPv6 address 2001:0:200:3::/127, unused bit",
.valid = 0,
.data = {
0x20,
0x01,
0x00,
0x00,
0x02,
0x00,
0x00,
0x00,
0x00,
0x03,
0x00,
0x00,
0x00,
0x00,
0x00,
0x01,
},
.length = 16,
.unused_bits = 1,
},
{
.descr = "RFC 3779, 2.1.1, IPv6 address 2001:0:200:3::/39",
.valid = 1,
.data = {
0x20,
0x01,
0x00,
0x00,
0x02,
},
.length = 5,
.unused_bits = 1,
.der = {
0x03,
0x06,
0x01,
0x20,
0x01,
0x00,
0x00,
0x02,
},
.der_len = 8,
},
};
static int
abs_set1_test(const struct abs_set1_test *tbl, int idx)
{
const struct abs_set1_test *test = &tbl[idx];
ASN1_BIT_STRING *abs = NULL;
unsigned char *der = NULL;
int ret, der_len = 0;
int success = 0;
if (!TEST_ptr(abs = ASN1_BIT_STRING_new())) {
TEST_info("%s: (idx = %d) %s ASN1_BIT_STRING_new()", __func__, idx, test->descr);
goto err;
}
ret = ASN1_BIT_STRING_set1(abs, test->data, test->length, test->unused_bits);
if (!TEST_int_eq(ret, test->valid)) {
TEST_info("%s: (idx = %d) %s ASN1_BIT_STRING_set1(): want %d, got %d",
__func__, idx, test->descr, test->valid, ret);
goto err;
}
if (!test->valid)
goto done;
der = NULL;
if (!TEST_int_eq((der_len = i2d_ASN1_BIT_STRING(abs, &der)), test->der_len)) {
TEST_info("%s: (idx=%d), %s i2d_ASN1_BIT_STRING(): want %d, got %d",
__func__, idx, test->descr, test->der_len, der_len);
if (der_len < 0)
der_len = 0;
goto err;
}
if (!TEST_mem_eq(der, der_len, test->der, test->der_len)) {
TEST_info("%s: (idx = %d) %s DER mismatch", __func__, idx, test->descr);
goto err;
}
done:
success = 1;
err:
ASN1_BIT_STRING_free(abs);
OPENSSL_clear_free(der, der_len);
return success;
}
static int
asn1_bit_string_set1_test(int idx)
{
return abs_set1_test(abs_set1_tests, idx);
}
int setup_tests(void)
{
ADD_ALL_TESTS(asn1_bit_string_get_length_test, OSSL_NELEM(abs_get_length_tests));
ADD_ALL_TESTS(asn1_bit_string_set1_test, OSSL_NELEM(abs_set1_tests));
return 1;
}
+1
View File
@@ -5703,3 +5703,4 @@ EVP_MD_CTX_serialize ? 4_0_0 EXIST::FUNCTION:
EVP_MD_CTX_deserialize ? 4_0_0 EXIST::FUNCTION:
OSSL_ENCODER_CTX_ctrl_string ? 4_0_0 EXIST::FUNCTION:
OPENSSL_sk_set_cmp_thunks ? 4_0_0 EXIST::FUNCTION:
ASN1_BIT_STRING_set1 ? 4_0_0 EXIST::FUNCTION: