#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);
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);
+}
=head1 NAME
+ASN1_BIT_STRING_set1,
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);
+ 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.
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;
}