summaryrefslogtreecommitdiffstats
path: root/src/crypto/bytestring
diff options
context:
space:
mode:
authorAdam Langley <agl@google.com>2015-01-22 14:27:53 -0800
committerAdam Langley <agl@google.com>2015-01-30 16:52:14 -0800
commitd9e397b599b13d642138480a28c14db7a136bf05 (patch)
tree34bab61dc4ce323b123ad4614dbc07e86ea2f9ef /src/crypto/bytestring
downloadexternal_boringssl-d9e397b599b13d642138480a28c14db7a136bf05.zip
external_boringssl-d9e397b599b13d642138480a28c14db7a136bf05.tar.gz
external_boringssl-d9e397b599b13d642138480a28c14db7a136bf05.tar.bz2
Initial commit of BoringSSL for Android.
Diffstat (limited to 'src/crypto/bytestring')
-rw-r--r--src/crypto/bytestring/CMakeLists.txt19
-rw-r--r--src/crypto/bytestring/ber.c219
-rw-r--r--src/crypto/bytestring/bytestring_test.c655
-rw-r--r--src/crypto/bytestring/cbb.c375
-rw-r--r--src/crypto/bytestring/cbs.c388
-rw-r--r--src/crypto/bytestring/internal.h46
6 files changed, 1702 insertions, 0 deletions
diff --git a/src/crypto/bytestring/CMakeLists.txt b/src/crypto/bytestring/CMakeLists.txt
new file mode 100644
index 0000000..8d6be7b
--- /dev/null
+++ b/src/crypto/bytestring/CMakeLists.txt
@@ -0,0 +1,19 @@
+include_directories(. .. ../../include)
+
+add_library(
+ bytestring
+
+ OBJECT
+
+ ber.c
+ cbs.c
+ cbb.c
+)
+
+add_executable(
+ bytestring_test
+
+ bytestring_test.c
+)
+
+target_link_libraries(bytestring_test crypto)
diff --git a/src/crypto/bytestring/ber.c b/src/crypto/bytestring/ber.c
new file mode 100644
index 0000000..2729fa1
--- /dev/null
+++ b/src/crypto/bytestring/ber.c
@@ -0,0 +1,219 @@
+/* Copyright (c) 2014, Google Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
+
+#include <openssl/bytestring.h>
+
+#include <string.h>
+
+#include "internal.h"
+
+
+/* kMaxDepth is a just a sanity limit. The code should be such that the length
+ * of the input being processes always decreases. None the less, a very large
+ * input could otherwise cause the stack to overflow. */
+static const unsigned kMaxDepth = 2048;
+
+/* cbs_find_ber walks an ASN.1 structure in |orig_in| and sets |*ber_found|
+ * depending on whether an indefinite length element was found. The value of
+ * |in| is not changed. It returns one on success (i.e. |*ber_found| was set)
+ * and zero on error. */
+static int cbs_find_ber(CBS *orig_in, char *ber_found, unsigned depth) {
+ CBS in;
+
+ if (depth > kMaxDepth) {
+ return 0;
+ }
+
+ CBS_init(&in, CBS_data(orig_in), CBS_len(orig_in));
+ *ber_found = 0;
+
+ while (CBS_len(&in) > 0) {
+ CBS contents;
+ unsigned tag;
+ size_t header_len;
+
+ if (!CBS_get_any_asn1_element(&in, &contents, &tag, &header_len)) {
+ return 0;
+ }
+ if (CBS_len(&contents) == header_len &&
+ header_len > 0 &&
+ CBS_data(&contents)[header_len-1] == 0x80) {
+ *ber_found = 1;
+ return 1;
+ }
+ if (tag & CBS_ASN1_CONSTRUCTED) {
+ if (!CBS_skip(&contents, header_len) ||
+ !cbs_find_ber(&contents, ber_found, depth + 1)) {
+ return 0;
+ }
+ }
+ }
+
+ return 1;
+}
+
+/* is_primitive_type returns true if |tag| likely a primitive type. Normally
+ * one can just test the "constructed" bit in the tag but, in BER, even
+ * primitive tags can have the constructed bit if they have indefinite
+ * length. */
+static char is_primitive_type(unsigned tag) {
+ return (tag & 0xc0) == 0 &&
+ (tag & 0x1f) != (CBS_ASN1_SEQUENCE & 0x1f) &&
+ (tag & 0x1f) != (CBS_ASN1_SET & 0x1f);
+}
+
+/* is_eoc returns true if |header_len| and |contents|, as returned by
+ * |CBS_get_any_asn1_element|, indicate an "end of contents" (EOC) value. */
+static char is_eoc(size_t header_len, CBS *contents) {
+ return header_len == 2 && CBS_len(contents) == 2 &&
+ memcmp(CBS_data(contents), "\x00\x00", 2) == 0;
+}
+
+/* cbs_convert_ber reads BER data from |in| and writes DER data to |out|. If
+ * |squash_header| is set then the top-level of elements from |in| will not
+ * have their headers written. This is used when concatenating the fragments of
+ * an indefinite length, primitive value. If |looking_for_eoc| is set then any
+ * EOC elements found will cause the function to return after consuming it.
+ * It returns one on success and zero on error. */
+static int cbs_convert_ber(CBS *in, CBB *out, char squash_header,
+ char looking_for_eoc, unsigned depth) {
+ if (depth > kMaxDepth) {
+ return 0;
+ }
+
+ while (CBS_len(in) > 0) {
+ CBS contents;
+ unsigned tag;
+ size_t header_len;
+ CBB *out_contents, out_contents_storage;
+
+ if (!CBS_get_any_asn1_element(in, &contents, &tag, &header_len)) {
+ return 0;
+ }
+ out_contents = out;
+
+ if (CBS_len(&contents) == header_len) {
+ if (is_eoc(header_len, &contents)) {
+ return looking_for_eoc;
+ }
+
+ if (header_len > 0 && CBS_data(&contents)[header_len - 1] == 0x80) {
+ /* This is an indefinite length element. If it's a SEQUENCE or SET then
+ * we just need to write the out the contents as normal, but with a
+ * concrete length prefix.
+ *
+ * If it's a something else then the contents will be a series of BER
+ * elements of the same type which need to be concatenated. */
+ const char context_specific = (tag & 0xc0) == 0x80;
+ char squash_child_headers = is_primitive_type(tag);
+
+ /* This is a hack, but it sufficies to handle NSS's output. If we find
+ * an indefinite length, context-specific tag with a definite, primtive
+ * tag inside it, then we assume that the context-specific tag is
+ * implicit and the tags within are fragments of a primitive type that
+ * need to be concatenated. */
+ if (context_specific && (tag & CBS_ASN1_CONSTRUCTED)) {
+ CBS in_copy, inner_contents;
+ unsigned inner_tag;
+ size_t inner_header_len;
+
+ CBS_init(&in_copy, CBS_data(in), CBS_len(in));
+ if (!CBS_get_any_asn1_element(&in_copy, &inner_contents, &inner_tag,
+ &inner_header_len)) {
+ return 0;
+ }
+ if (CBS_len(&inner_contents) > inner_header_len &&
+ is_primitive_type(inner_tag)) {
+ squash_child_headers = 1;
+ }
+ }
+
+ if (!squash_header) {
+ unsigned out_tag = tag;
+ if (squash_child_headers) {
+ out_tag &= ~CBS_ASN1_CONSTRUCTED;
+ }
+ if (!CBB_add_asn1(out, &out_contents_storage, out_tag)) {
+ return 0;
+ }
+ out_contents = &out_contents_storage;
+ }
+
+ if (!cbs_convert_ber(in, out_contents,
+ squash_child_headers,
+ 1 /* looking for eoc */, depth + 1)) {
+ return 0;
+ }
+ if (out_contents != out && !CBB_flush(out)) {
+ return 0;
+ }
+ continue;
+ }
+ }
+
+ if (!squash_header) {
+ if (!CBB_add_asn1(out, &out_contents_storage, tag)) {
+ return 0;
+ }
+ out_contents = &out_contents_storage;
+ }
+
+ if (!CBS_skip(&contents, header_len)) {
+ return 0;
+ }
+
+ if (tag & CBS_ASN1_CONSTRUCTED) {
+ if (!cbs_convert_ber(&contents, out_contents, 0 /* don't squash header */,
+ 0 /* not looking for eoc */, depth + 1)) {
+ return 0;
+ }
+ } else {
+ if (!CBB_add_bytes(out_contents, CBS_data(&contents),
+ CBS_len(&contents))) {
+ return 0;
+ }
+ }
+
+ if (out_contents != out && !CBB_flush(out)) {
+ return 0;
+ }
+ }
+
+ return looking_for_eoc == 0;
+}
+
+int CBS_asn1_ber_to_der(CBS *in, uint8_t **out, size_t *out_len) {
+ CBB cbb;
+
+ /* First, do a quick walk to find any indefinite-length elements. Most of the
+ * time we hope that there aren't any and thus we can quickly return. */
+ char conversion_needed;
+ if (!cbs_find_ber(in, &conversion_needed, 0)) {
+ return 0;
+ }
+
+ if (!conversion_needed) {
+ *out = NULL;
+ *out_len = 0;
+ return 1;
+ }
+
+ CBB_init(&cbb, CBS_len(in));
+ if (!cbs_convert_ber(in, &cbb, 0, 0, 0)) {
+ CBB_cleanup(&cbb);
+ return 0;
+ }
+
+ return CBB_finish(&cbb, out, out_len);
+}
diff --git a/src/crypto/bytestring/bytestring_test.c b/src/crypto/bytestring/bytestring_test.c
new file mode 100644
index 0000000..cd0155e
--- /dev/null
+++ b/src/crypto/bytestring/bytestring_test.c
@@ -0,0 +1,655 @@
+/* Copyright (c) 2014, Google Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <openssl/crypto.h>
+#include <openssl/bytestring.h>
+
+#include "internal.h"
+#include "../internal.h"
+
+
+static int test_skip(void) {
+ static const uint8_t kData[] = {1, 2, 3};
+ CBS data;
+
+ CBS_init(&data, kData, sizeof(kData));
+ return CBS_len(&data) == 3 &&
+ CBS_skip(&data, 1) &&
+ CBS_len(&data) == 2 &&
+ CBS_skip(&data, 2) &&
+ CBS_len(&data) == 0 &&
+ !CBS_skip(&data, 1);
+}
+
+static int test_get_u(void) {
+ static const uint8_t kData[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
+ uint8_t u8;
+ uint16_t u16;
+ uint32_t u32;
+ CBS data;
+
+ CBS_init(&data, kData, sizeof(kData));
+ return CBS_get_u8(&data, &u8) &&
+ u8 == 1 &&
+ CBS_get_u16(&data, &u16) &&
+ u16 == 0x203 &&
+ CBS_get_u24(&data, &u32) &&
+ u32 == 0x40506 &&
+ CBS_get_u32(&data, &u32) &&
+ u32 == 0x708090a &&
+ !CBS_get_u8(&data, &u8);
+}
+
+static int test_get_prefixed(void) {
+ static const uint8_t kData[] = {1, 2, 0, 2, 3, 4, 0, 0, 3, 3, 2, 1};
+ uint8_t u8;
+ uint16_t u16;
+ uint32_t u32;
+ CBS data, prefixed;
+
+ CBS_init(&data, kData, sizeof(kData));
+ return CBS_get_u8_length_prefixed(&data, &prefixed) &&
+ CBS_len(&prefixed) == 1 &&
+ CBS_get_u8(&prefixed, &u8) &&
+ u8 == 2 &&
+ CBS_get_u16_length_prefixed(&data, &prefixed) &&
+ CBS_len(&prefixed) == 2 &&
+ CBS_get_u16(&prefixed, &u16) &&
+ u16 == 0x304 &&
+ CBS_get_u24_length_prefixed(&data, &prefixed) &&
+ CBS_len(&prefixed) == 3 &&
+ CBS_get_u24(&prefixed, &u32) &&
+ u32 == 0x30201;
+}
+
+static int test_get_prefixed_bad(void) {
+ static const uint8_t kData1[] = {2, 1};
+ static const uint8_t kData2[] = {0, 2, 1};
+ static const uint8_t kData3[] = {0, 0, 2, 1};
+ CBS data, prefixed;
+
+ CBS_init(&data, kData1, sizeof(kData1));
+ if (CBS_get_u8_length_prefixed(&data, &prefixed)) {
+ return 0;
+ }
+
+ CBS_init(&data, kData2, sizeof(kData2));
+ if (CBS_get_u16_length_prefixed(&data, &prefixed)) {
+ return 0;
+ }
+
+ CBS_init(&data, kData3, sizeof(kData3));
+ if (CBS_get_u24_length_prefixed(&data, &prefixed)) {
+ return 0;
+ }
+
+ return 1;
+}
+
+static int test_get_asn1(void) {
+ static const uint8_t kData1[] = {0x30, 2, 1, 2};
+ static const uint8_t kData2[] = {0x30, 3, 1, 2};
+ static const uint8_t kData3[] = {0x30, 0x80};
+ static const uint8_t kData4[] = {0x30, 0x81, 1, 1};
+ static const uint8_t kData5[] = {0x30, 0x82, 0, 1, 1};
+ static const uint8_t kData6[] = {0xa1, 3, 0x4, 1, 1};
+ static const uint8_t kData7[] = {0xa1, 3, 0x4, 2, 1};
+ static const uint8_t kData8[] = {0xa1, 3, 0x2, 1, 1};
+ static const uint8_t kData9[] = {0xa1, 3, 0x2, 1, 0xff};
+
+ CBS data, contents;
+ int present;
+ uint64_t value;
+
+ CBS_init(&data, kData1, sizeof(kData1));
+ if (CBS_peek_asn1_tag(&data, 0x1) ||
+ !CBS_peek_asn1_tag(&data, 0x30)) {
+ return 0;
+ }
+ if (!CBS_get_asn1(&data, &contents, 0x30) ||
+ CBS_len(&contents) != 2 ||
+ memcmp(CBS_data(&contents), "\x01\x02", 2) != 0) {
+ return 0;
+ }
+
+ CBS_init(&data, kData2, sizeof(kData2));
+ /* data is truncated */
+ if (CBS_get_asn1(&data, &contents, 0x30)) {
+ return 0;
+ }
+
+ CBS_init(&data, kData3, sizeof(kData3));
+ /* zero byte length of length */
+ if (CBS_get_asn1(&data, &contents, 0x30)) {
+ return 0;
+ }
+
+ CBS_init(&data, kData4, sizeof(kData4));
+ /* long form mistakenly used. */
+ if (CBS_get_asn1(&data, &contents, 0x30)) {
+ return 0;
+ }
+
+ CBS_init(&data, kData5, sizeof(kData5));
+ /* length takes too many bytes. */
+ if (CBS_get_asn1(&data, &contents, 0x30)) {
+ return 0;
+ }
+
+ CBS_init(&data, kData1, sizeof(kData1));
+ /* wrong tag. */
+ if (CBS_get_asn1(&data, &contents, 0x31)) {
+ return 0;
+ }
+
+ CBS_init(&data, NULL, 0);
+ /* peek at empty data. */
+ if (CBS_peek_asn1_tag(&data, 0x30)) {
+ return 0;
+ }
+
+ CBS_init(&data, NULL, 0);
+ /* optional elements at empty data. */
+ if (!CBS_get_optional_asn1(&data, &contents, &present, 0xa0) ||
+ present ||
+ !CBS_get_optional_asn1_octet_string(&data, &contents, &present, 0xa0) ||
+ present ||
+ CBS_len(&contents) != 0 ||
+ !CBS_get_optional_asn1_octet_string(&data, &contents, NULL, 0xa0) ||
+ CBS_len(&contents) != 0 ||
+ !CBS_get_optional_asn1_uint64(&data, &value, 0xa0, 42) ||
+ value != 42) {
+ return 0;
+ }
+
+ CBS_init(&data, kData6, sizeof(kData6));
+ /* optional element. */
+ if (!CBS_get_optional_asn1(&data, &contents, &present, 0xa0) ||
+ present ||
+ !CBS_get_optional_asn1(&data, &contents, &present, 0xa1) ||
+ !present ||
+ CBS_len(&contents) != 3 ||
+ memcmp(CBS_data(&contents), "\x04\x01\x01", 3) != 0) {
+ return 0;
+ }
+
+ CBS_init(&data, kData6, sizeof(kData6));
+ /* optional octet string. */
+ if (!CBS_get_optional_asn1_octet_string(&data, &contents, &present, 0xa0) ||
+ present ||
+ CBS_len(&contents) != 0 ||
+ !CBS_get_optional_asn1_octet_string(&data, &contents, &present, 0xa1) ||
+ !present ||
+ CBS_len(&contents) != 1 ||
+ CBS_data(&contents)[0] != 1) {
+ return 0;
+ }
+
+ CBS_init(&data, kData7, sizeof(kData7));
+ /* invalid optional octet string. */
+ if (CBS_get_optional_asn1_octet_string(&data, &contents, &present, 0xa1)) {
+ return 0;
+ }
+
+ CBS_init(&data, kData8, sizeof(kData8));
+ /* optional octet string. */
+ if (!CBS_get_optional_asn1_uint64(&data, &value, 0xa0, 42) ||
+ value != 42 ||
+ !CBS_get_optional_asn1_uint64(&data, &value, 0xa1, 42) ||
+ value != 1) {
+ return 0;
+ }
+
+ CBS_init(&data, kData9, sizeof(kData9));
+ /* invalid optional integer. */
+ if (CBS_get_optional_asn1_uint64(&data, &value, 0xa1, 42)) {
+ return 0;
+ }
+
+ return 1;
+}
+
+static int test_get_optional_asn1_bool(void) {
+ CBS data;
+ int val;
+
+ static const uint8_t kTrue[] = {0x0a, 3, CBS_ASN1_BOOLEAN, 1, 0xff};
+ static const uint8_t kFalse[] = {0x0a, 3, CBS_ASN1_BOOLEAN, 1, 0x00};
+ static const uint8_t kInvalid[] = {0x0a, 3, CBS_ASN1_BOOLEAN, 1, 0x01};
+
+ CBS_init(&data, NULL, 0);
+ val = 2;
+ if (!CBS_get_optional_asn1_bool(&data, &val, 0x0a, 0) ||
+ val != 0) {
+ return 0;
+ }
+
+ CBS_init(&data, kTrue, sizeof(kTrue));
+ val = 2;
+ if (!CBS_get_optional_asn1_bool(&data, &val, 0x0a, 0) ||
+ val != 1) {
+ return 0;
+ }
+
+ CBS_init(&data, kFalse, sizeof(kFalse));
+ val = 2;
+ if (!CBS_get_optional_asn1_bool(&data, &val, 0x0a, 1) ||
+ val != 0) {
+ return 0;
+ }
+
+ CBS_init(&data, kInvalid, sizeof(kInvalid));
+ if (CBS_get_optional_asn1_bool(&data, &val, 0x0a, 1)) {
+ return 0;
+ }
+
+ return 1;
+}
+
+static int test_cbb_basic(void) {
+ static const uint8_t kExpected[] = {1, 2, 3, 4, 5, 6, 7, 8};
+ uint8_t *buf;
+ size_t buf_len;
+ int ok;
+ CBB cbb;
+
+ if (!CBB_init(&cbb, 100)) {
+ return 0;
+ }
+ CBB_cleanup(&cbb);
+
+ if (!CBB_init(&cbb, 0) ||
+ !CBB_add_u8(&cbb, 1) ||
+ !CBB_add_u16(&cbb, 0x203) ||
+ !CBB_add_u24(&cbb, 0x40506) ||
+ !CBB_add_bytes(&cbb, (const uint8_t*) "\x07\x08", 2) ||
+ !CBB_finish(&cbb, &buf, &buf_len)) {
+ return 0;
+ }
+
+ ok = buf_len == sizeof(kExpected) && memcmp(buf, kExpected, buf_len) == 0;
+ free(buf);
+ return ok;
+}
+
+static int test_cbb_fixed(void) {
+ CBB cbb;
+ uint8_t buf[1];
+ uint8_t *out_buf;
+ size_t out_size;
+
+ if (!CBB_init_fixed(&cbb, NULL, 0) ||
+ CBB_add_u8(&cbb, 1) ||
+ !CBB_finish(&cbb, &out_buf, &out_size) ||
+ out_buf != NULL ||
+ out_size != 0) {
+ return 0;
+ }
+
+ if (!CBB_init_fixed(&cbb, buf, 1) ||
+ !CBB_add_u8(&cbb, 1) ||
+ CBB_add_u8(&cbb, 2) ||
+ !CBB_finish(&cbb, &out_buf, &out_size) ||
+ out_buf != buf ||
+ out_size != 1 ||
+ buf[0] != 1) {
+ return 0;
+ }
+
+ return 1;
+}
+
+static int test_cbb_finish_child(void) {
+ CBB cbb, child;
+ uint8_t *out_buf;
+ size_t out_size;
+
+ if (!CBB_init(&cbb, 16) ||
+ !CBB_add_u8_length_prefixed(&cbb, &child) ||
+ CBB_finish(&child, &out_buf, &out_size) ||
+ !CBB_finish(&cbb, &out_buf, &out_size) ||
+ out_size != 1 ||
+ out_buf[0] != 0) {
+ return 0;
+ }
+
+ free(out_buf);
+ return 1;
+}
+
+static int test_cbb_prefixed(void) {
+ static const uint8_t kExpected[] = {0, 1, 1, 0, 2, 2, 3, 0, 0, 3,
+ 4, 5, 6, 5, 4, 1, 0, 1, 2};
+ uint8_t *buf;
+ size_t buf_len;
+ CBB cbb, contents, inner_contents, inner_inner_contents;
+ int ok;
+
+ if (!CBB_init(&cbb, 0) ||
+ !CBB_add_u8_length_prefixed(&cbb, &contents) ||
+ !CBB_add_u8_length_prefixed(&cbb, &contents) ||
+ !CBB_add_u8(&contents, 1) ||
+ !CBB_add_u16_length_prefixed(&cbb, &contents) ||
+ !CBB_add_u16(&contents, 0x203) ||
+ !CBB_add_u24_length_prefixed(&cbb, &contents) ||
+ !CBB_add_u24(&contents, 0x40506) ||
+ !CBB_add_u8_length_prefixed(&cbb, &contents) ||
+ !CBB_add_u8_length_prefixed(&contents, &inner_contents) ||
+ !CBB_add_u8(&inner_contents, 1) ||
+ !CBB_add_u16_length_prefixed(&inner_contents, &inner_inner_contents) ||
+ !CBB_add_u8(&inner_inner_contents, 2) ||
+ !CBB_finish(&cbb, &buf, &buf_len)) {
+ return 0;
+ }
+
+ ok = buf_len == sizeof(kExpected) && memcmp(buf, kExpected, buf_len) == 0;
+ free(buf);
+ return ok;
+}
+
+static int test_cbb_misuse(void) {
+ CBB cbb, child, contents;
+ uint8_t *buf;
+ size_t buf_len;
+
+ if (!CBB_init(&cbb, 0) ||
+ !CBB_add_u8_length_prefixed(&cbb, &child) ||
+ !CBB_add_u8(&child, 1) ||
+ !CBB_add_u8(&cbb, 2)) {
+ return 0;
+ }
+
+ /* Since we wrote to |cbb|, |child| is now invalid and attempts to write to
+ * it should fail. */
+ if (CBB_add_u8(&child, 1) ||
+ CBB_add_u16(&child, 1) ||
+ CBB_add_u24(&child, 1) ||
+ CBB_add_u8_length_prefixed(&child, &contents) ||
+ CBB_add_u16_length_prefixed(&child, &contents) ||
+ CBB_add_asn1(&child, &contents, 1) ||
+ CBB_add_bytes(&child, (const uint8_t*) "a", 1)) {
+ fprintf(stderr, "CBB operation on invalid CBB did not fail.\n");
+ return 0;
+ }
+
+ if (!CBB_finish(&cbb, &buf, &buf_len) ||
+ buf_len != 3 ||
+ memcmp(buf, "\x01\x01\x02", 3) != 0) {
+ return 0;
+ }
+
+ free(buf);
+
+ return 1;
+}
+
+static int test_cbb_asn1(void) {
+ static const uint8_t kExpected[] = {0x30, 3, 1, 2, 3};
+ uint8_t *buf, *test_data;
+ size_t buf_len;
+ CBB cbb, contents, inner_contents;
+
+ if (!CBB_init(&cbb, 0) ||
+ !CBB_add_asn1(&cbb, &contents, 0x30) ||
+ !CBB_add_bytes(&contents, (const uint8_t*) "\x01\x02\x03", 3) ||
+ !CBB_finish(&cbb, &buf, &buf_len)) {
+ return 0;
+ }
+
+ if (buf_len != sizeof(kExpected) || memcmp(buf, kExpected, buf_len) != 0) {
+ return 0;
+ }
+ free(buf);
+
+ test_data = malloc(100000);
+ memset(test_data, 0x42, 100000);
+
+ if (!CBB_init(&cbb, 0) ||
+ !CBB_add_asn1(&cbb, &contents, 0x30) ||
+ !CBB_add_bytes(&contents, test_data, 130) ||
+ !CBB_finish(&cbb, &buf, &buf_len)) {
+ return 0;
+ }
+
+ if (buf_len != 3 + 130 ||
+ memcmp(buf, "\x30\x81\x82", 3) != 0 ||
+ memcmp(buf + 3, test_data, 130) != 0) {
+ return 0;
+ }
+ free(buf);
+
+ if (!CBB_init(&cbb, 0) ||
+ !CBB_add_asn1(&cbb, &contents, 0x30) ||
+ !CBB_add_bytes(&contents, test_data, 1000) ||
+ !CBB_finish(&cbb, &buf, &buf_len)) {
+ return 0;
+ }
+
+ if (buf_len != 4 + 1000 ||
+ memcmp(buf, "\x30\x82\x03\xe8", 4) != 0 ||
+ memcmp(buf + 4, test_data, 1000)) {
+ return 0;
+ }
+ free(buf);
+
+ if (!CBB_init(&cbb, 0) ||
+ !CBB_add_asn1(&cbb, &contents, 0x30) ||
+ !CBB_add_asn1(&contents, &inner_contents, 0x30) ||
+ !CBB_add_bytes(&inner_contents, test_data, 100000) ||
+ !CBB_finish(&cbb, &buf, &buf_len)) {
+ return 0;
+ }
+
+ if (buf_len != 5 + 5 + 100000 ||
+ memcmp(buf, "\x30\x83\x01\x86\xa5\x30\x83\x01\x86\xa0", 10) != 0 ||
+ memcmp(buf + 10, test_data, 100000)) {
+ return 0;
+ }
+ free(buf);
+
+ free(test_data);
+ return 1;
+}
+
+static int do_ber_convert(const char *name,
+ const uint8_t *der_expected, size_t der_len,
+ const uint8_t *ber, size_t ber_len) {
+ CBS in;
+ uint8_t *out;
+ size_t out_len;
+
+ CBS_init(&in, ber, ber_len);
+ if (!CBS_asn1_ber_to_der(&in, &out, &out_len)) {
+ fprintf(stderr, "%s: CBS_asn1_ber_to_der failed.\n", name);
+ return 0;
+ }
+
+ if (out == NULL) {
+ if (ber_len != der_len ||
+ memcmp(der_expected, ber, ber_len) != 0) {
+ fprintf(stderr, "%s: incorrect unconverted result.\n", name);
+ return 0;
+ }
+
+ return 1;
+ }
+
+ if (out_len != der_len ||
+ memcmp(out, der_expected, der_len) != 0) {
+ fprintf(stderr, "%s: incorrect converted result.\n", name);
+ return 0;
+ }
+
+ free(out);
+ return 1;
+}
+
+static int test_ber_convert(void) {
+ static const uint8_t kSimpleBER[] = {0x01, 0x01, 0x00};
+
+ /* kIndefBER contains a SEQUENCE with an indefinite length. */
+ static const uint8_t kIndefBER[] = {0x30, 0x80, 0x01, 0x01, 0x02, 0x00, 0x00};
+ static const uint8_t kIndefDER[] = {0x30, 0x03, 0x01, 0x01, 0x02};
+
+ /* kOctetStringBER contains an indefinite length OCTETSTRING with two parts.
+ * These parts need to be concatenated in DER form. */
+ static const uint8_t kOctetStringBER[] = {0x24, 0x80, 0x04, 0x02, 0, 1,
+ 0x04, 0x02, 2, 3, 0x00, 0x00};
+ static const uint8_t kOctetStringDER[] = {0x04, 0x04, 0, 1, 2, 3};
+
+ /* kNSSBER is part of a PKCS#12 message generated by NSS that uses indefinite
+ * length elements extensively. */
+ static const uint8_t kNSSBER[] = {
+ 0x30, 0x80, 0x02, 0x01, 0x03, 0x30, 0x80, 0x06, 0x09, 0x2a, 0x86, 0x48,
+ 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x01, 0xa0, 0x80, 0x24, 0x80, 0x04, 0x04,
+ 0x01, 0x02, 0x03, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x39,
+ 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x05,
+ 0x00, 0x04, 0x14, 0x84, 0x98, 0xfc, 0x66, 0x33, 0xee, 0xba, 0xe7, 0x90,
+ 0xc1, 0xb6, 0xe8, 0x8f, 0xfe, 0x1d, 0xc5, 0xa5, 0x97, 0x93, 0x3e, 0x04,
+ 0x10, 0x38, 0x62, 0xc6, 0x44, 0x12, 0xd5, 0x30, 0x00, 0xf8, 0xf2, 0x1b,
+ 0xf0, 0x6e, 0x10, 0x9b, 0xb8, 0x02, 0x02, 0x07, 0xd0, 0x00, 0x00,
+ };
+
+ static const uint8_t kNSSDER[] = {
+ 0x30, 0x53, 0x02, 0x01, 0x03, 0x30, 0x13, 0x06, 0x09, 0x2a, 0x86,
+ 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x01, 0xa0, 0x06, 0x04, 0x04,
+ 0x01, 0x02, 0x03, 0x04, 0x30, 0x39, 0x30, 0x21, 0x30, 0x09, 0x06,
+ 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14, 0x84,
+ 0x98, 0xfc, 0x66, 0x33, 0xee, 0xba, 0xe7, 0x90, 0xc1, 0xb6, 0xe8,
+ 0x8f, 0xfe, 0x1d, 0xc5, 0xa5, 0x97, 0x93, 0x3e, 0x04, 0x10, 0x38,
+ 0x62, 0xc6, 0x44, 0x12, 0xd5, 0x30, 0x00, 0xf8, 0xf2, 0x1b, 0xf0,
+ 0x6e, 0x10, 0x9b, 0xb8, 0x02, 0x02, 0x07, 0xd0,
+ };
+
+ return do_ber_convert("kSimpleBER", kSimpleBER, sizeof(kSimpleBER),
+ kSimpleBER, sizeof(kSimpleBER)) &&
+ do_ber_convert("kIndefBER", kIndefDER, sizeof(kIndefDER), kIndefBER,
+ sizeof(kIndefBER)) &&
+ do_ber_convert("kOctetStringBER", kOctetStringDER,
+ sizeof(kOctetStringDER), kOctetStringBER,
+ sizeof(kOctetStringBER)) &&
+ do_ber_convert("kNSSBER", kNSSDER, sizeof(kNSSDER), kNSSBER,
+ sizeof(kNSSBER));
+}
+
+typedef struct {
+ uint64_t value;
+ const char *encoding;
+ size_t encoding_len;
+} ASN1_UINT64_TEST;
+
+static const ASN1_UINT64_TEST kAsn1Uint64Tests[] = {
+ {0, "\x02\x01\x00", 3},
+ {1, "\x02\x01\x01", 3},
+ {127, "\x02\x01\x7f", 3},
+ {128, "\x02\x02\x00\x80", 4},
+ {0xdeadbeef, "\x02\x05\x00\xde\xad\xbe\xef", 7},
+ {OPENSSL_U64(0x0102030405060708),
+ "\x02\x08\x01\x02\x03\x04\x05\x06\x07\x08", 10},
+ {OPENSSL_U64(0xffffffffffffffff),
+ "\x02\x09\x00\xff\xff\xff\xff\xff\xff\xff\xff", 11},
+};
+
+typedef struct {
+ const char *encoding;
+ size_t encoding_len;
+} ASN1_INVALID_UINT64_TEST;
+
+static const ASN1_INVALID_UINT64_TEST kAsn1InvalidUint64Tests[] = {
+ /* Bad tag. */
+ {"\x03\x01\x00", 3},
+ /* Empty contents. */
+ {"\x02\x00", 2},
+ /* Negative number. */
+ {"\x02\x01\x80", 3},
+ /* Overflow */
+ {"\x02\x09\x01\x00\x00\x00\x00\x00\x00\x00\x00", 11},
+};
+
+static int test_asn1_uint64(void) {
+ size_t i;
+
+ for (i = 0; i < sizeof(kAsn1Uint64Tests) / sizeof(kAsn1Uint64Tests[0]); i++) {
+ const ASN1_UINT64_TEST *test = &kAsn1Uint64Tests[i];
+ CBS cbs;
+ uint64_t value;
+ CBB cbb;
+ uint8_t *out;
+ size_t len;
+
+ CBS_init(&cbs, (const uint8_t *)test->encoding, test->encoding_len);
+ if (!CBS_get_asn1_uint64(&cbs, &value) ||
+ CBS_len(&cbs) != 0 ||
+ value != test->value) {
+ return 0;
+ }
+
+ if (!CBB_init(&cbb, 0)) {
+ return 0;
+ }
+ if (!CBB_add_asn1_uint64(&cbb, test->value) ||
+ !CBB_finish(&cbb, &out, &len)) {
+ CBB_cleanup(&cbb);
+ return 0;
+ }
+ if (len != test->encoding_len || memcmp(out, test->encoding, len) != 0) {
+ free(out);
+ return 0;
+ }
+ free(out);
+ }
+
+ for (i = 0;
+ i < sizeof(kAsn1InvalidUint64Tests) / sizeof(kAsn1InvalidUint64Tests[0]);
+ i++) {
+ const ASN1_INVALID_UINT64_TEST *test = &kAsn1InvalidUint64Tests[i];
+ CBS cbs;
+ uint64_t value;
+
+ CBS_init(&cbs, (const uint8_t *)test->encoding, test->encoding_len);
+ if (CBS_get_asn1_uint64(&cbs, &value)) {
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+int main(void) {
+ CRYPTO_library_init();
+
+ if (!test_skip() ||
+ !test_get_u() ||
+ !test_get_prefixed() ||
+ !test_get_prefixed_bad() ||
+ !test_get_asn1() ||
+ !test_cbb_basic() ||
+ !test_cbb_fixed() ||
+ !test_cbb_finish_child() ||
+ !test_cbb_misuse() ||
+ !test_cbb_prefixed() ||
+ !test_cbb_asn1() ||
+ !test_ber_convert() ||
+ !test_asn1_uint64() ||
+ !test_get_optional_asn1_bool()) {
+ return 1;
+ }
+
+ printf("PASS\n");
+ return 0;
+}
diff --git a/src/crypto/bytestring/cbb.c b/src/crypto/bytestring/cbb.c
new file mode 100644
index 0000000..4428836
--- /dev/null
+++ b/src/crypto/bytestring/cbb.c
@@ -0,0 +1,375 @@
+/* Copyright (c) 2014, Google Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
+
+#include <openssl/bytestring.h>
+
+#include <assert.h>
+#include <string.h>
+
+#include <openssl/mem.h>
+
+
+static int cbb_init(CBB *cbb, uint8_t *buf, size_t cap) {
+ struct cbb_buffer_st *base;
+
+ base = OPENSSL_malloc(sizeof(struct cbb_buffer_st));
+ if (base == NULL) {
+ OPENSSL_free(buf);
+ return 0;
+ }
+
+ base->buf = buf;
+ base->len = 0;
+ base->cap = cap;
+ base->can_resize = 1;
+
+ memset(cbb, 0, sizeof(CBB));
+ cbb->base = base;
+ cbb->is_top_level = 1;
+ return 1;
+}
+
+int CBB_init(CBB *cbb, size_t initial_capacity) {
+ uint8_t *buf;
+
+ buf = OPENSSL_malloc(initial_capacity);
+ if (initial_capacity > 0 && buf == NULL) {
+ return 0;
+ }
+
+ return cbb_init(cbb, buf, initial_capacity);
+}
+
+int CBB_init_fixed(CBB *cbb, uint8_t *buf, size_t len) {
+ if (!cbb_init(cbb, buf, len)) {
+ return 0;
+ }
+
+ cbb->base->can_resize = 0;
+ return 1;
+}
+
+void CBB_cleanup(CBB *cbb) {
+ if (cbb->base) {
+ if (cbb->base->buf && cbb->base->can_resize) {
+ OPENSSL_free(cbb->base->buf);
+ }
+ OPENSSL_free(cbb->base);
+ }
+ cbb->base = NULL;
+}
+
+static int cbb_buffer_add(struct cbb_buffer_st *base, uint8_t **out,
+ size_t len) {
+ size_t newlen;
+
+ if (base == NULL) {
+ return 0;
+ }
+
+ newlen = base->len + len;
+ if (newlen < base->len) {
+ /* Overflow */
+ return 0;
+ }
+
+ if (newlen > base->cap) {
+ size_t newcap = base->cap * 2;
+ uint8_t *newbuf;
+
+ if (!base->can_resize) {
+ return 0;
+ }
+
+ if (newcap < base->cap || newcap < newlen) {
+ newcap = newlen;
+ }
+ newbuf = OPENSSL_realloc(base->buf, newcap);
+ if (newbuf == NULL) {
+ return 0;
+ }
+
+ base->buf = newbuf;
+ base->cap = newcap;
+ }
+
+ if (out) {
+ *out = base->buf + base->len;
+ }
+ base->len = newlen;
+ return 1;
+}
+
+static int cbb_buffer_add_u(struct cbb_buffer_st *base, uint32_t v,
+ size_t len_len) {
+ uint8_t *buf;
+ size_t i;
+
+ if (len_len == 0) {
+ return 1;
+ }
+ if (!cbb_buffer_add(base, &buf, len_len)) {
+ return 0;
+ }
+
+ for (i = len_len - 1; i < len_len; i--) {
+ buf[i] = v;
+ v >>= 8;
+ }
+ return 1;
+}
+
+int CBB_finish(CBB *cbb, uint8_t **out_data, size_t *out_len) {
+ if (!cbb->is_top_level) {
+ return 0;
+ }
+
+ if (!CBB_flush(cbb)) {
+ return 0;
+ }
+
+ if (cbb->base->can_resize && (out_data == NULL || out_len == NULL)) {
+ /* |out_data| and |out_len| can only be NULL if the CBB is fixed. */
+ return 0;
+ }
+
+ if (out_data != NULL) {
+ *out_data = cbb->base->buf;
+ }
+ if (out_len != NULL) {
+ *out_len = cbb->base->len;
+ }
+ cbb->base->buf = NULL;
+ CBB_cleanup(cbb);
+ return 1;
+}
+
+/* CBB_flush recurses and then writes out any pending length prefix. The
+ * current length of the underlying base is taken to be the length of the
+ * length-prefixed data. */
+int CBB_flush(CBB *cbb) {
+ size_t child_start, i, len;
+
+ if (cbb->base == NULL) {
+ return 0;
+ }
+
+ if (cbb->child == NULL || cbb->pending_len_len == 0) {
+ return 1;
+ }
+
+ child_start = cbb->offset + cbb->pending_len_len;
+
+ if (!CBB_flush(cbb->child) ||
+ child_start < cbb->offset ||
+ cbb->base->len < child_start) {
+ return 0;
+ }
+
+ len = cbb->base->len - child_start;
+
+ if (cbb->pending_is_asn1) {
+ /* For ASN.1 we assume that we'll only need a single byte for the length.
+ * If that turned out to be incorrect, we have to move the contents along
+ * in order to make space. */
+ size_t len_len;
+ uint8_t initial_length_byte;
+
+ assert (cbb->pending_len_len == 1);
+
+ if (len > 0xfffffffe) {
+ /* Too large. */
+ return 0;
+ } else if (len > 0xffffff) {
+ len_len = 5;
+ initial_length_byte = 0x80 | 4;
+ } else if (len > 0xffff) {
+ len_len = 4;
+ initial_length_byte = 0x80 | 3;
+ } else if (len > 0xff) {
+ len_len = 3;
+ initial_length_byte = 0x80 | 2;
+ } else if (len > 0x7f) {
+ len_len = 2;
+ initial_length_byte = 0x80 | 1;
+ } else {
+ len_len = 1;
+ initial_length_byte = len;
+ len = 0;
+ }
+
+ if (len_len != 1) {
+ /* We need to move the contents along in order to make space. */
+ size_t extra_bytes = len_len - 1;
+ if (!cbb_buffer_add(cbb->base, NULL, extra_bytes)) {
+ return 0;
+ }
+ memmove(cbb->base->buf + child_start + extra_bytes,
+ cbb->base->buf + child_start, len);
+ }
+ cbb->base->buf[cbb->offset++] = initial_length_byte;
+ cbb->pending_len_len = len_len - 1;
+ }
+
+ for (i = cbb->pending_len_len - 1; i < cbb->pending_len_len; i--) {
+ cbb->base->buf[cbb->offset + i] = len;
+ len >>= 8;
+ }
+ if (len != 0) {
+ return 0;
+ }
+
+ cbb->child->base = NULL;
+ cbb->child = NULL;
+ cbb->pending_len_len = 0;
+ cbb->pending_is_asn1 = 0;
+ cbb->offset = 0;
+
+ return 1;
+}
+
+
+static int cbb_add_length_prefixed(CBB *cbb, CBB *out_contents,
+ size_t len_len) {
+ uint8_t *prefix_bytes;
+
+ if (!CBB_flush(cbb)) {
+ return 0;
+ }
+
+ cbb->offset = cbb->base->len;
+ if (!cbb_buffer_add(cbb->base, &prefix_bytes, len_len)) {
+ return 0;
+ }
+
+ memset(prefix_bytes, 0, len_len);
+ memset(out_contents, 0, sizeof(CBB));
+ out_contents->base = cbb->base;
+ cbb->child = out_contents;
+ cbb->pending_len_len = len_len;
+ cbb->pending_is_asn1 = 0;
+
+ return 1;
+}
+
+int CBB_add_u8_length_prefixed(CBB *cbb, CBB *out_contents) {
+ return cbb_add_length_prefixed(cbb, out_contents, 1);
+}
+
+int CBB_add_u16_length_prefixed(CBB *cbb, CBB *out_contents) {
+ return cbb_add_length_prefixed(cbb, out_contents, 2);
+}
+
+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, uint8_t tag) {
+ if (!CBB_flush(cbb) ||
+ !CBB_add_u8(cbb, tag)) {
+ return 0;
+ }
+
+ cbb->offset = cbb->base->len;
+ if (!CBB_add_u8(cbb, 0)) {
+ return 0;
+ }
+
+ memset(out_contents, 0, sizeof(CBB));
+ out_contents->base = cbb->base;
+ cbb->child = out_contents;
+ cbb->pending_len_len = 1;
+ cbb->pending_is_asn1 = 1;
+
+ return 1;
+}
+
+int CBB_add_bytes(CBB *cbb, const uint8_t *data, size_t len) {
+ uint8_t *dest;
+
+ if (!CBB_flush(cbb) ||
+ !cbb_buffer_add(cbb->base, &dest, len)) {
+ return 0;
+ }
+ memcpy(dest, data, len);
+ return 1;
+}
+
+int CBB_add_space(CBB *cbb, uint8_t **out_data, size_t len) {
+ if (!CBB_flush(cbb) ||
+ !cbb_buffer_add(cbb->base, out_data, len)) {
+ return 0;
+ }
+ return 1;
+}
+
+int CBB_add_u8(CBB *cbb, uint8_t value) {
+ if (!CBB_flush(cbb)) {
+ return 0;
+ }
+
+ return cbb_buffer_add_u(cbb->base, value, 1);
+}
+
+int CBB_add_u16(CBB *cbb, uint16_t value) {
+ if (!CBB_flush(cbb)) {
+ return 0;
+ }
+
+ return cbb_buffer_add_u(cbb->base, value, 2);
+}
+
+int CBB_add_u24(CBB *cbb, uint32_t value) {
+ if (!CBB_flush(cbb)) {
+ return 0;
+ }
+
+ return cbb_buffer_add_u(cbb->base, value, 3);
+}
+
+int CBB_add_asn1_uint64(CBB *cbb, uint64_t value) {
+ CBB child;
+ size_t i;
+ int started = 0;
+
+ if (!CBB_add_asn1(cbb, &child, CBS_ASN1_INTEGER)) {
+ return 0;
+ }
+
+ for (i = 0; i < 8; i++) {
+ uint8_t byte = (value >> 8*(7-i)) & 0xff;
+ if (!started) {
+ if (byte == 0) {
+ /* Don't encode leading zeros. */
+ continue;
+ }
+ /* If the high bit is set, add a padding byte to make it
+ * unsigned. */
+ if ((byte & 0x80) && !CBB_add_u8(&child, 0)) {
+ return 0;
+ }
+ started = 1;
+ }
+ if (!CBB_add_u8(&child, byte)) {
+ return 0;
+ }
+ }
+
+ /* 0 is encoded as a single 0, not the empty string. */
+ if (!started && !CBB_add_u8(&child, 0)) {
+ return 0;
+ }
+
+ return CBB_flush(cbb);
+}
diff --git a/src/crypto/bytestring/cbs.c b/src/crypto/bytestring/cbs.c
new file mode 100644
index 0000000..b417716
--- /dev/null
+++ b/src/crypto/bytestring/cbs.c
@@ -0,0 +1,388 @@
+/* Copyright (c) 2014, Google Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
+
+#include <openssl/buf.h>
+#include <openssl/mem.h>
+#include <openssl/bytestring.h>
+
+#include <assert.h>
+#include <string.h>
+
+#include "internal.h"
+
+
+void CBS_init(CBS *cbs, const uint8_t *data, size_t len) {
+ cbs->data = data;
+ cbs->len = len;
+}
+
+static int cbs_get(CBS *cbs, const uint8_t **p, size_t n) {
+ if (cbs->len < n) {
+ return 0;
+ }
+
+ *p = cbs->data;
+ cbs->data += n;
+ cbs->len -= n;
+ return 1;
+}
+
+int CBS_skip(CBS *cbs, size_t len) {
+ const uint8_t *dummy;
+ return cbs_get(cbs, &dummy, len);
+}
+
+const uint8_t *CBS_data(const CBS *cbs) {
+ return cbs->data;
+}
+
+size_t CBS_len(const CBS *cbs) {
+ return cbs->len;
+}
+
+int CBS_stow(const CBS *cbs, uint8_t **out_ptr, size_t *out_len) {
+ if (*out_ptr != NULL) {
+ OPENSSL_free(*out_ptr);
+ *out_ptr = NULL;
+ }
+ *out_len = 0;
+
+ if (cbs->len == 0) {
+ return 1;
+ }
+ *out_ptr = BUF_memdup(cbs->data, cbs->len);
+ if (*out_ptr == NULL) {
+ return 0;
+ }
+ *out_len = cbs->len;
+ return 1;
+}
+
+int CBS_strdup(const CBS *cbs, char **out_ptr) {
+ if (*out_ptr != NULL) {
+ OPENSSL_free(*out_ptr);
+ }
+ *out_ptr = BUF_strndup((const char*)cbs->data, cbs->len);
+ return (*out_ptr != NULL);
+}
+
+int CBS_contains_zero_byte(const CBS *cbs) {
+ return memchr(cbs->data, 0, cbs->len) != NULL;
+}
+
+int CBS_mem_equal(const CBS *cbs, const uint8_t *data, size_t len) {
+ if (len != cbs->len)
+ return 0;
+ return CRYPTO_memcmp(cbs->data, data, len) == 0;
+}
+
+static int cbs_get_u(CBS *cbs, uint32_t *out, size_t len) {
+ uint32_t result = 0;
+ size_t i;
+ const uint8_t *data;
+
+ if (!cbs_get(cbs, &data, len)) {
+ return 0;
+ }
+ for (i = 0; i < len; i++) {
+ result <<= 8;
+ result |= data[i];
+ }
+ *out = result;
+ return 1;
+}
+
+int CBS_get_u8(CBS *cbs, uint8_t *out) {
+ const uint8_t *v;
+ if (!cbs_get(cbs, &v, 1)) {
+ return 0;
+ }
+ *out = *v;
+ return 1;
+}
+
+int CBS_get_u16(CBS *cbs, uint16_t *out) {
+ uint32_t v;
+ if (!cbs_get_u(cbs, &v, 2)) {
+ return 0;
+ }
+ *out = v;
+ return 1;
+}
+
+int CBS_get_u24(CBS *cbs, uint32_t *out) {
+ return cbs_get_u(cbs, out, 3);
+}
+
+int CBS_get_u32(CBS *cbs, uint32_t *out) {
+ return cbs_get_u(cbs, out, 4);
+}
+
+int CBS_get_bytes(CBS *cbs, CBS *out, size_t len) {
+ const uint8_t *v;
+ if (!cbs_get(cbs, &v, len)) {
+ return 0;
+ }
+ CBS_init(out, v, len);
+ return 1;
+}
+
+static int cbs_get_length_prefixed(CBS *cbs, CBS *out, size_t len_len) {
+ uint32_t len;
+ if (!cbs_get_u(cbs, &len, len_len)) {
+ return 0;
+ }
+ return CBS_get_bytes(cbs, out, len);
+}
+
+int CBS_get_u8_length_prefixed(CBS *cbs, CBS *out) {
+ return cbs_get_length_prefixed(cbs, out, 1);
+}
+
+int CBS_get_u16_length_prefixed(CBS *cbs, CBS *out) {
+ return cbs_get_length_prefixed(cbs, out, 2);
+}
+
+int CBS_get_u24_length_prefixed(CBS *cbs, CBS *out) {
+ return cbs_get_length_prefixed(cbs, out, 3);
+}
+
+int CBS_get_any_asn1_element(CBS *cbs, CBS *out, unsigned *out_tag,
+ size_t *out_header_len) {
+ uint8_t tag, length_byte;
+ CBS header = *cbs;
+ CBS throwaway;
+
+ if (out == NULL) {
+ out = &throwaway;
+ }
+
+ if (!CBS_get_u8(&header, &tag) ||
+ !CBS_get_u8(&header, &length_byte)) {
+ return 0;
+ }
+
+ if ((tag & 0x1f) == 0x1f) {
+ /* Long form tags are not supported. */
+ return 0;
+ }
+
+ if (out_tag != NULL) {
+ *out_tag = tag;
+ }
+
+ size_t len;
+ if ((length_byte & 0x80) == 0) {
+ /* Short form length. */
+ len = ((size_t) length_byte) + 2;
+ if (out_header_len != NULL) {
+ *out_header_len = 2;
+ }
+ } else {
+ /* Long form length. */
+ const size_t num_bytes = length_byte & 0x7f;
+ uint32_t len32;
+
+ if ((tag & CBS_ASN1_CONSTRUCTED) != 0 && num_bytes == 0) {
+ /* indefinite length */
+ *out_header_len = 2;
+ return CBS_get_bytes(cbs, out, 2);
+ }
+
+ if (num_bytes == 0 || num_bytes > 4) {
+ return 0;
+ }
+ if (!cbs_get_u(&header, &len32, num_bytes)) {
+ return 0;
+ }
+ if (len32 < 128) {
+ /* Length should have used short-form encoding. */
+ return 0;
+ }
+ if ((len32 >> ((num_bytes-1)*8)) == 0) {
+ /* Length should have been at least one byte shorter. */
+ return 0;
+ }
+ len = len32;
+ if (len + 2 + num_bytes < len) {
+ /* Overflow. */
+ return 0;
+ }
+ len += 2 + num_bytes;
+ if (out_header_len != NULL) {
+ *out_header_len = 2 + num_bytes;
+ }
+ }
+
+ return CBS_get_bytes(cbs, out, len);
+}
+
+static int cbs_get_asn1(CBS *cbs, CBS *out, unsigned tag_value,
+ int skip_header) {
+ size_t header_len;
+ unsigned tag;
+ CBS throwaway;
+
+ if (out == NULL) {
+ out = &throwaway;
+ }
+
+ if (!CBS_get_any_asn1_element(cbs, out, &tag, &header_len) ||
+ tag != tag_value ||
+ (header_len > 0 &&
+ /* This ensures that the tag is either zero length or
+ * indefinite-length. */
+ CBS_len(out) == header_len &&
+ CBS_data(out)[header_len - 1] == 0x80)) {
+ return 0;
+ }
+
+ if (skip_header && !CBS_skip(out, header_len)) {
+ assert(0);
+ return 0;
+ }
+
+ return 1;
+}
+
+int CBS_get_asn1(CBS *cbs, CBS *out, unsigned tag_value) {
+ return cbs_get_asn1(cbs, out, tag_value, 1 /* skip header */);
+}
+
+int CBS_get_asn1_element(CBS *cbs, CBS *out, unsigned tag_value) {
+ return cbs_get_asn1(cbs, out, tag_value, 0 /* include header */);
+}
+
+int CBS_peek_asn1_tag(const CBS *cbs, unsigned tag_value) {
+ if (CBS_len(cbs) < 1) {
+ return 0;
+ }
+ return CBS_data(cbs)[0] == tag_value;
+}
+
+int CBS_get_asn1_uint64(CBS *cbs, uint64_t *out) {
+ CBS bytes;
+ const uint8_t *data;
+ size_t i, len;
+
+ if (!CBS_get_asn1(cbs, &bytes, CBS_ASN1_INTEGER)) {
+ return 0;
+ }
+
+ *out = 0;
+ data = CBS_data(&bytes);
+ len = CBS_len(&bytes);
+
+ if (len == 0) {
+ /* An INTEGER is encoded with at least one octet. */
+ return 0;
+ }
+
+ if ((data[0] & 0x80) != 0) {
+ /* negative number */
+ return 0;
+ }
+
+ for (i = 0; i < len; i++) {
+ if ((*out >> 56) != 0) {
+ /* Too large to represent as a uint64_t. */
+ return 0;
+ }
+ *out <<= 8;
+ *out |= data[i];
+ }
+
+ return 1;
+}
+
+int CBS_get_optional_asn1(CBS *cbs, CBS *out, int *out_present, unsigned tag) {
+ if (CBS_peek_asn1_tag(cbs, tag)) {
+ if (!CBS_get_asn1(cbs, out, tag)) {
+ return 0;
+ }
+ *out_present = 1;
+ } else {
+ *out_present = 0;
+ }
+ return 1;
+}
+
+int CBS_get_optional_asn1_octet_string(CBS *cbs, CBS *out, int *out_present,
+ unsigned tag) {
+ CBS child;
+ int present;
+ if (!CBS_get_optional_asn1(cbs, &child, &present, tag)) {
+ return 0;
+ }
+ if (present) {
+ if (!CBS_get_asn1(&child, out, CBS_ASN1_OCTETSTRING) ||
+ CBS_len(&child) != 0) {
+ return 0;
+ }
+ } else {
+ CBS_init(out, NULL, 0);
+ }
+ if (out_present) {
+ *out_present = present;
+ }
+ return 1;
+}
+
+int CBS_get_optional_asn1_uint64(CBS *cbs, uint64_t *out, unsigned tag,
+ uint64_t default_value) {
+ CBS child;
+ int present;
+ if (!CBS_get_optional_asn1(cbs, &child, &present, tag)) {
+ return 0;
+ }
+ if (present) {
+ if (!CBS_get_asn1_uint64(&child, out) ||
+ CBS_len(&child) != 0) {
+ return 0;
+ }
+ } else {
+ *out = default_value;
+ }
+ return 1;
+}
+
+int CBS_get_optional_asn1_bool(CBS *cbs, int *out, unsigned tag,
+ int default_value) {
+ CBS child, child2;
+ int present;
+ if (!CBS_get_optional_asn1(cbs, &child, &present, tag)) {
+ return 0;
+ }
+ if (present) {
+ uint8_t boolean;
+
+ if (!CBS_get_asn1(&child, &child2, CBS_ASN1_BOOLEAN) ||
+ CBS_len(&child2) != 1 ||
+ CBS_len(&child) != 0) {
+ return 0;
+ }
+
+ boolean = CBS_data(&child2)[0];
+ if (boolean == 0) {
+ *out = 0;
+ } else if (boolean == 0xff) {
+ *out = 1;
+ } else {
+ return 0;
+ }
+ } else {
+ *out = default_value;
+ }
+ return 1;
+}
diff --git a/src/crypto/bytestring/internal.h b/src/crypto/bytestring/internal.h
new file mode 100644
index 0000000..b4ea7e5
--- /dev/null
+++ b/src/crypto/bytestring/internal.h
@@ -0,0 +1,46 @@
+/* Copyright (c) 2014, Google Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
+
+#ifndef OPENSSL_HEADER_BYTESTRING_INTERNAL_H
+#define OPENSSL_HEADER_BYTESTRING_INTERNAL_H
+
+#include <openssl/base.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+
+/* CBS_asn1_ber_to_der reads an ASN.1 structure from |in|. If it finds
+ * indefinite-length elements then it attempts to convert the BER data to DER
+ * and sets |*out| and |*out_length| to describe a malloced buffer containing
+ * the DER data. Additionally, |*in| will be advanced over the ASN.1 data.
+ *
+ * If it doesn't find any indefinite-length elements then it sets |*out| to
+ * NULL and |*in| is unmodified.
+ *
+ * A sufficiently complex ASN.1 structure will break this function because it's
+ * not possible to generically convert BER to DER without knowledge of the
+ * structure itself. However, this sufficies to handle the PKCS#7 and #12 output
+ * from NSS.
+ *
+ * It returns one on success and zero otherwise. */
+OPENSSL_EXPORT int CBS_asn1_ber_to_der(CBS *in, uint8_t **out, size_t *out_len);
+
+
+#if defined(__cplusplus)
+} /* extern C */
+#endif
+
+#endif /* OPENSSL_HEADER_BYTESTRING_INTERNAL_H */