mirror of
https://github.com/openssl/openssl.git
synced 2026-05-07 20:12:39 +00:00
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:
committed by
Neil Horman
parent
2ea6e785f5
commit
74d47c8e66
@@ -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*
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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 */
|
||||
{-
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user