Add CBS functions for working with BIT STRINGs.

Querying a bit in a BIT STRING is a little finicky. Add some functions
to help with this.

Change-Id: I813b9b6f2d952d61d8717b47bca1344f0ad4b7d1
Reviewed-on: https://boringssl-review.googlesource.com/12800
Reviewed-by: Adam Langley <agl@google.com>
Commit-Queue: Adam Langley <agl@google.com>
CQ-Verified: CQ bot account: commit-bot@chromium.org <commit-bot@chromium.org>
This commit is contained in:
David Benjamin 2016-12-13 20:48:19 -05:00 committed by CQ bot account: commit-bot@chromium.org
parent aa01204175
commit fcd714d52b
3 changed files with 124 additions and 1 deletions

View File

@ -889,6 +889,82 @@ static bool TestStickyError() {
return true;
}
static bool TestBitString() {
static const std::vector<uint8_t> kValidBitStrings[] = {
{0x00}, // 0 bits
{0x07, 0x80}, // 1 bit
{0x04, 0xf0}, // 4 bits
{0x00, 0xff}, // 8 bits
{0x06, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0}, // 42 bits
};
for (const auto& test : kValidBitStrings) {
CBS cbs;
CBS_init(&cbs, test.data(), test.size());
if (!CBS_is_valid_asn1_bitstring(&cbs)) {
return false;
}
}
static const std::vector<uint8_t> kInvalidBitStrings[] = {
// BIT STRINGs always have a leading byte.
{},
// It's not possible to take an unused bit off the empty string.
{0x01},
// There can be at most 7 unused bits.
{0x08, 0xff},
{0xff, 0xff},
// All unused bits must be cleared.
{0x06, 0xff, 0xc1},
};
for (const auto& test : kInvalidBitStrings) {
CBS cbs;
CBS_init(&cbs, test.data(), test.size());
if (CBS_is_valid_asn1_bitstring(&cbs)) {
return false;
}
// CBS_asn1_bitstring_has_bit returns false on invalid inputs.
if (CBS_asn1_bitstring_has_bit(&cbs, 0)) {
return false;
}
}
static const struct {
std::vector<uint8_t> in;
unsigned bit;
bool bit_set;
} kBitTests[] = {
// Basic tests.
{{0x00}, 0, false},
{{0x07, 0x80}, 0, true},
{{0x06, 0x0f, 0x40}, 0, false},
{{0x06, 0x0f, 0x40}, 1, false},
{{0x06, 0x0f, 0x40}, 2, false},
{{0x06, 0x0f, 0x40}, 3, false},
{{0x06, 0x0f, 0x40}, 4, true},
{{0x06, 0x0f, 0x40}, 5, true},
{{0x06, 0x0f, 0x40}, 6, true},
{{0x06, 0x0f, 0x40}, 7, true},
{{0x06, 0x0f, 0x40}, 8, false},
{{0x06, 0x0f, 0x40}, 9, true},
// Out-of-bounds bits return 0.
{{0x06, 0x0f, 0x40}, 10, false},
{{0x06, 0x0f, 0x40}, 15, false},
{{0x06, 0x0f, 0x40}, 16, false},
{{0x06, 0x0f, 0x40}, 1000, false},
};
for (const auto& test : kBitTests) {
CBS cbs;
CBS_init(&cbs, test.in.data(), test.in.size());
if (CBS_asn1_bitstring_has_bit(&cbs, test.bit) !=
static_cast<int>(test.bit_set)) {
return false;
}
}
return true;
}
int main() {
CRYPTO_library_init();
@ -910,7 +986,8 @@ int main() {
!TestGetOptionalASN1Bool() ||
!TestZero() ||
!TestCBBReserve() ||
!TestStickyError()) {
!TestStickyError() ||
!TestBitString()) {
return 1;
}

View File

@ -451,3 +451,40 @@ int CBS_get_optional_asn1_bool(CBS *cbs, int *out, unsigned tag,
}
return 1;
}
int CBS_is_valid_asn1_bitstring(const CBS *cbs) {
CBS in = *cbs;
uint8_t num_unused_bits;
if (!CBS_get_u8(&in, &num_unused_bits) ||
num_unused_bits > 7) {
return 0;
}
if (num_unused_bits == 0) {
return 1;
}
/* All num_unused_bits bits must exist and be zeros. */
uint8_t last;
if (!CBS_get_last_u8(&in, &last) ||
(last & ((1 << num_unused_bits) - 1)) != 0) {
return 0;
}
return 1;
}
int CBS_asn1_bitstring_has_bit(const CBS *cbs, unsigned bit) {
if (!CBS_is_valid_asn1_bitstring(cbs)) {
return 0;
}
const unsigned byte_num = (bit >> 3) + 1;
const unsigned bit_num = 7 - (bit & 7);
/* Unused bits are zero, and this function does not distinguish between
* missing and unset bits. Thus it is sufficient to do a byte-level length
* check. */
return byte_num < CBS_len(cbs) &&
(CBS_data(cbs)[byte_num] & (1 << bit_num)) != 0;
}

View File

@ -257,6 +257,15 @@ OPENSSL_EXPORT int CBS_get_optional_asn1_uint64(CBS *cbs, uint64_t *out,
OPENSSL_EXPORT int CBS_get_optional_asn1_bool(CBS *cbs, int *out, unsigned tag,
int default_value);
/* CBS_is_valid_asn1_bitstring returns one if |cbs| is a valid ASN.1 BIT STRING
* and zero otherwise. */
OPENSSL_EXPORT int CBS_is_valid_asn1_bitstring(const CBS *cbs);
/* CBS_asn1_bitstring_has_bit returns one if |cbs| is a valid ASN.1 BIT STRING
* and the specified bit is present and set. Otherwise, it returns zero. |bit|
* is indexed starting from zero. */
OPENSSL_EXPORT int CBS_asn1_bitstring_has_bit(const CBS *cbs, unsigned bit);
/* CRYPTO ByteBuilder.
*