diff options
author | David Benjamin <davidben@google.com> | 2021-06-19 16:28:04 -0400 |
---|---|---|
committer | Boringssl LUCI CQ <boringssl-scoped@luci-project-accounts.iam.gserviceaccount.com> | 2021-06-22 23:42:40 +0000 |
commit | 52067828463443821e175975d19085b2c8bf2f54 (patch) | |
tree | ac9a421063e84c1a3e23146bbe18af71097bb23e /include | |
parent | cafb9921149dd713ce2c2de2d057664575bedd93 (diff) | |
download | boringssl-52067828463443821e175975d19085b2c8bf2f54.zip boringssl-52067828463443821e175975d19085b2c8bf2f54.tar.gz boringssl-52067828463443821e175975d19085b2c8bf2f54.tar.bz2 |
Compute ASN.1 BIT STRING sizes more consistently.
OpenSSL's BIT STRING representation has two modes, one where it
implicitly trims trailing zeros and the other where the number of unused
bits is explicitly set. This means logic in ASN1_item_verify, or
elsewhere in callers, that checks flags and ASN1_STRING_length is
inconsistent with i2c_ASN1_BIT_STRING.
Add ASN1_BIT_STRING_num_bytes for code that needs to deal with X.509
using BIT STRING for some fields instead of OCTET STRING. Switch
ASN1_item_verify to it. Some external code does this too, so export it
as public API.
This is mostly a theoretical issue. All parsed BIT STRINGS use explicit
byte strings, and there are no APIs (apart from not-yet-opaquified
structs) to specify the ASN1_STRING in X509, etc., structures. We
intentionally made X509_set1_signature_value, etc., internally construct
the ASN1_STRING. Still having an API is more consistent and helps nudge
callers towards rejecting excess bits when they want bytes.
It may also be worth a public API for consistently accessing the bit
count. I've left it alone for now because I've not seen callers that
need it, and it saves worrying about bytes-to-bits overflows.
This also fixes a bug in the original version of the truncating logic
when the entire string was all zeros, and const-corrects a few
parameters.
Change-Id: I9d29842a3d3264b0cde61ca8cfea07d02177dbc2
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/48225
Commit-Queue: David Benjamin <davidben@google.com>
Commit-Queue: Adam Langley <agl@google.com>
Reviewed-by: Adam Langley <agl@google.com>
Diffstat (limited to 'include')
-rw-r--r-- | include/openssl/asn1.h | 94 | ||||
-rw-r--r-- | include/openssl/x509.h | 4 |
2 files changed, 80 insertions, 18 deletions
diff --git a/include/openssl/asn1.h b/include/openssl/asn1.h index 0347d0d..fe2f29d 100644 --- a/include/openssl/asn1.h +++ b/include/openssl/asn1.h @@ -177,13 +177,7 @@ extern "C" { // string. // // When representing a BIT STRING value, the type field is |V_ASN1_BIT_STRING|. -// The data contains the encoded form of the BIT STRING, including any padding -// bits added to round to a whole number of bytes, but excluding the leading -// byte containing the number of padding bits. The number of padding bits is -// encoded in the flags field. See |ASN1_STRING_FLAG_BITS_LEFT| for details. For -// example, DER encodes the BIT STRING {1, 0} as {0x06, 0x80 = 0b10_000000}. The -// |ASN1_STRING| representation has data of {0x80} and flags of -// ASN1_STRING_FLAG_BITS_LEFT | 6. +// See bit string documentation below for how the data and flags are used. // // When representing an INTEGER or ENUMERATED value, the data contains the // big-endian encoding of the absolute value of the integer. The sign bit is @@ -308,8 +302,83 @@ OPENSSL_EXPORT int ASN1_STRING_set(ASN1_STRING *str, const void *data, int len); // |OPENSSL_malloc|. OPENSSL_EXPORT void ASN1_STRING_set0(ASN1_STRING *str, void *data, int len); -// TODO(davidben): Pull up and document functions specific to individual string -// types. +// TODO(davidben): Expand and document function prototypes generated in macros. + + +// Bit strings. +// +// An ASN.1 BIT STRING type represents a string of bits. The string may not +// necessarily be a whole number of bytes. BIT STRINGs occur in ASN.1 structures +// in several forms: +// +// Some BIT STRINGs represent a bitmask of named bits, such as the X.509 key +// usage extension in RFC5280, section 4.2.1.3. For such bit strings, DER +// imposes an additional restriction that trailing zero bits are removed. Some +// functions like |ASN1_BIT_STRING_set_bit| help in maintaining this. +// +// Other BIT STRINGs are arbitrary strings of bits used as identifiers and do +// not have this constraint, such as the X.509 issuerUniqueID field. +// +// Finally, some structures use BIT STRINGs as a container for byte strings. For +// example, the signatureValue field in X.509 and the subjectPublicKey field in +// SubjectPublicKeyInfo are defined as BIT STRINGs with a value specific to the +// AlgorithmIdentifier. While some unknown algorithm could choose to store +// arbitrary bit strings, all supported algorithms use a byte string, with bit +// order matching the DER encoding. Callers interpreting a BIT STRING as a byte +// string should use |ASN1_BIT_STRING_num_bytes| instead of |ASN1_STRING_length| +// and reject bit strings that are not a whole number of bytes. +// +// This library represents BIT STRINGs as |ASN1_STRING|s with type +// |V_ASN1_BIT_STRING|. The data contains the encoded form of the BIT STRING, +// including any padding bits added to round to a whole number of bytes, but +// excluding the leading byte containing the number of padding bits. If +// |ASN1_STRING_FLAG_BITS_LEFT| is set, the bottom three bits contains the +// number of padding bits. For example, DER encodes the BIT STRING {1, 0} as +// {0x06, 0x80 = 0b10_000000}. The |ASN1_STRING| representation has data of +// {0x80} and flags of ASN1_STRING_FLAG_BITS_LEFT | 6. If +// |ASN1_STRING_FLAG_BITS_LEFT| is unset, trailing zero bits are implicitly +// removed. Callers should not rely this representation when constructing bit +// strings. + +// ASN1_BIT_STRING_num_bytes computes the length of |str| in bytes. If |str|'s +// bit length is a multiple of 8, it sets |*out| to the byte length and returns +// one. Otherwise, it returns zero. +// +// This function may be used with |ASN1_STRING_get0_data| to interpret |str| as +// a byte string. +OPENSSL_EXPORT int ASN1_BIT_STRING_num_bytes(const ASN1_BIT_STRING *str, + size_t *out); + +// ASN1_BIT_STRING_set calls |ASN1_STRING_set|. It leaves flags unchanged, so +// the caller must set the number of unused bits. +// +// TODO(davidben): Maybe it should? Wrapping a byte string in a bit string is a +// common use case. +OPENSSL_EXPORT int ASN1_BIT_STRING_set(ASN1_BIT_STRING *str, + const unsigned char *d, int length); + +// ASN1_BIT_STRING_set_bit sets bit |n| of |str| to one if |value| is non-zero +// and zero if |value| is zero, resizing |str| as needed. It then truncates +// trailing zeros in |str| to align with the DER represention for a bit string +// with named bits. It returns one on success and zero on error. |n| is indexed +// beginning from zero. +OPENSSL_EXPORT int ASN1_BIT_STRING_set_bit(ASN1_BIT_STRING *str, int n, + int value); + +// ASN1_BIT_STRING_get_bit returns one if bit |n| of |a| is in bounds and set, +// and zero otherwise. |n| is indexed beginning from zero. +OPENSSL_EXPORT int ASN1_BIT_STRING_get_bit(const ASN1_BIT_STRING *str, int n); + +// ASN1_BIT_STRING_check returns one if |str| only contains bits that are set in +// the |flags_len| bytes pointed by |flags|. Otherwise it returns zero. Bits in +// |flags| are arranged according to the DER representation, so bit 0 +// corresponds to the MSB of |flags[0]|. +OPENSSL_EXPORT int ASN1_BIT_STRING_check(const ASN1_BIT_STRING *str, + const unsigned char *flags, + int flags_len); + +// TODO(davidben): Expand and document function prototypes generated in macros. + // Arbitrary elements. @@ -799,13 +868,6 @@ OPENSSL_EXPORT int i2c_ASN1_BIT_STRING(const ASN1_BIT_STRING *a, OPENSSL_EXPORT ASN1_BIT_STRING *c2i_ASN1_BIT_STRING(ASN1_BIT_STRING **a, const unsigned char **pp, long length); -OPENSSL_EXPORT int ASN1_BIT_STRING_set(ASN1_BIT_STRING *a, unsigned char *d, - int length); -OPENSSL_EXPORT int ASN1_BIT_STRING_set_bit(ASN1_BIT_STRING *a, int n, - int value); -OPENSSL_EXPORT int ASN1_BIT_STRING_get_bit(const ASN1_BIT_STRING *a, int n); -OPENSSL_EXPORT int ASN1_BIT_STRING_check(const ASN1_BIT_STRING *a, - unsigned char *flags, int flags_len); OPENSSL_EXPORT int i2d_ASN1_BOOLEAN(int a, unsigned char **pp); OPENSSL_EXPORT int d2i_ASN1_BOOLEAN(int *a, const unsigned char **pp, diff --git a/include/openssl/x509.h b/include/openssl/x509.h index d06e1c6..97b3ccb 100644 --- a/include/openssl/x509.h +++ b/include/openssl/x509.h @@ -1075,8 +1075,8 @@ OPENSSL_EXPORT int ASN1_item_digest(const ASN1_ITEM *it, const EVP_MD *type, unsigned int *len); OPENSSL_EXPORT int ASN1_item_verify(const ASN1_ITEM *it, X509_ALGOR *algor1, - ASN1_BIT_STRING *signature, void *data, - EVP_PKEY *pkey); + const ASN1_BIT_STRING *signature, + void *data, EVP_PKEY *pkey); OPENSSL_EXPORT int ASN1_item_sign(const ASN1_ITEM *it, X509_ALGOR *algor1, X509_ALGOR *algor2, |