summaryrefslogtreecommitdiff
path: root/src/crypto/bytestring/cbb.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/crypto/bytestring/cbb.c')
-rw-r--r--src/crypto/bytestring/cbb.c128
1 files changed, 101 insertions, 27 deletions
diff --git a/src/crypto/bytestring/cbb.c b/src/crypto/bytestring/cbb.c
index b1afe7d5..f8f5e0f1 100644
--- a/src/crypto/bytestring/cbb.c
+++ b/src/crypto/bytestring/cbb.c
@@ -15,6 +15,7 @@
#include <openssl/bytestring.h>
#include <assert.h>
+#include <limits.h>
#include <string.h>
#include <openssl/mem.h>
@@ -328,37 +329,44 @@ int CBB_add_u24_length_prefixed(CBB *cbb, CBB *out_contents) {
return cbb_add_length_prefixed(cbb, out_contents, 3);
}
-int CBB_add_asn1(CBB *cbb, CBB *out_contents, unsigned tag) {
- if (!CBB_flush(cbb)) {
- return 0;
+// add_base128_integer encodes |v| as a big-endian base-128 integer where the
+// high bit of each byte indicates where there is more data. This is the
+// encoding used in DER for both high tag number form and OID components.
+static int add_base128_integer(CBB *cbb, uint64_t v) {
+ unsigned len_len = 0;
+ uint64_t copy = v;
+ while (copy > 0) {
+ len_len++;
+ copy >>= 7;
}
-
- // Split the tag into leading bits and tag number.
- uint8_t tag_bits = (tag >> CBS_ASN1_TAG_SHIFT) & 0xe0;
- unsigned tag_number = tag & CBS_ASN1_TAG_NUMBER_MASK;
- if (tag_number >= 0x1f) {
- // Set all the bits in the tag number to signal high tag number form.
- if (!CBB_add_u8(cbb, tag_bits | 0x1f)) {
+ if (len_len == 0) {
+ len_len = 1; // Zero is encoded with one byte.
+ }
+ for (unsigned i = len_len - 1; i < len_len; i--) {
+ uint8_t byte = (v >> (7 * i)) & 0x7f;
+ if (i != 0) {
+ // The high bit denotes whether there is more data.
+ byte |= 0x80;
+ }
+ if (!CBB_add_u8(cbb, byte)) {
return 0;
}
+ }
+ return 1;
+}
- unsigned len_len = 0;
- unsigned copy = tag_number;
- while (copy > 0) {
- len_len++;
- copy >>= 7;
- }
- for (unsigned i = len_len - 1; i < len_len; i--) {
- uint8_t byte = (tag_number >> (7 * i)) & 0x7f;
- if (i != 0) {
- // The high bit denotes whether there is more data.
- byte |= 0x80;
- }
- if (!CBB_add_u8(cbb, byte)) {
- return 0;
- }
- }
- } else if (!CBB_add_u8(cbb, tag_bits | tag_number)) {
+int CBB_add_asn1(CBB *cbb, CBB *out_contents, unsigned tag) {
+ if (tag > 0xff ||
+ (tag & 0x1f) == 0x1f) {
+ // Long form identifier octets are not supported. Further, all current valid
+ // tag serializations are 8 bits.
+ cbb->base->error = 1;
+ return 0;
+ }
+
+ if (!CBB_flush(cbb) ||
+ // |tag|'s representation matches the DER encoding.
+ !CBB_add_u8(cbb, (uint8_t)tag)) {
return 0;
}
@@ -492,3 +500,69 @@ int CBB_add_asn1_uint64(CBB *cbb, uint64_t value) {
return CBB_flush(cbb);
}
+
+// parse_dotted_decimal parses one decimal component from |cbs|, where |cbs| is
+// an OID literal, e.g., "1.2.840.113554.4.1.72585". It consumes both the
+// component and the dot, so |cbs| may be passed into the function again for the
+// next value.
+static int parse_dotted_decimal(CBS *cbs, uint64_t *out) {
+ *out = 0;
+ int seen_digit = 0;
+ for (;;) {
+ // Valid terminators for a component are the end of the string or a
+ // non-terminal dot. If the string ends with a dot, this is not a valid OID
+ // string.
+ uint8_t u;
+ if (!CBS_get_u8(cbs, &u) ||
+ (u == '.' && CBS_len(cbs) > 0)) {
+ break;
+ }
+ if (u < '0' || u > '9' ||
+ // Forbid stray leading zeros.
+ (seen_digit && *out == 0) ||
+ // Check for overflow.
+ *out > UINT64_MAX / 10 ||
+ *out * 10 > UINT64_MAX - (u - '0')) {
+ return 0;
+ }
+ *out = *out * 10 + (u - '0');
+ seen_digit = 1;
+ }
+ // The empty string is not a legal OID component.
+ return seen_digit;
+}
+
+int CBB_add_asn1_oid_from_text(CBB *cbb, const char *text, size_t len) {
+ if (!CBB_flush(cbb)) {
+ return 0;
+ }
+
+ CBS cbs;
+ CBS_init(&cbs, (const uint8_t *)text, len);
+
+ // OIDs must have at least two components.
+ uint64_t a, b;
+ if (!parse_dotted_decimal(&cbs, &a) ||
+ !parse_dotted_decimal(&cbs, &b)) {
+ return 0;
+ }
+
+ // The first component is encoded as 40 * |a| + |b|. This assumes that |a| is
+ // 0, 1, or 2 and that, when it is 0 or 1, |b| is at most 39.
+ if (a > 2 ||
+ (a < 2 && b > 39) ||
+ b > UINT64_MAX - 80 ||
+ !add_base128_integer(cbb, 40u * a + b)) {
+ return 0;
+ }
+
+ // The remaining components are encoded unmodified.
+ while (CBS_len(&cbs) > 0) {
+ if (!parse_dotted_decimal(&cbs, &a) ||
+ !add_base128_integer(cbb, a)) {
+ return 0;
+ }
+ }
+
+ return 1;
+}