external/boringssl: Sync to a5462d3050ac6a68ab488450bf5856475dbef992.
This includes the following changes:
https://boringssl.googlesource.com/boringssl/+log/27bc0f26c8d132df04f5b0b173aefeb8aaa13c33..a5462d3050ac6a68ab488450bf5856475dbef992
Test: BoringSSL CTS Presubmits
Change-Id: Ieac8258ca12c1fcbdc00196d7d3f3fc0635f94e3
diff --git a/src/crypto/asn1/a_object.c b/src/crypto/asn1/a_object.c
index a710add..005e37d 100644
--- a/src/crypto/asn1/a_object.c
+++ b/src/crypto/asn1/a_object.c
@@ -87,134 +87,6 @@
return (objsize);
}
-int a2d_ASN1_OBJECT(unsigned char *out, int olen, const char *buf, int num)
-{
- int i, first, len = 0, c, use_bn;
- char ftmp[24], *tmp = ftmp;
- int tmpsize = sizeof ftmp;
- const char *p;
- unsigned long l;
- BIGNUM *bl = NULL;
-
- if (num == 0)
- return (0);
- else if (num == -1)
- num = strlen(buf);
-
- p = buf;
- c = *(p++);
- num--;
- if ((c >= '0') && (c <= '2')) {
- first = c - '0';
- } else {
- OPENSSL_PUT_ERROR(ASN1, ASN1_R_FIRST_NUM_TOO_LARGE);
- goto err;
- }
-
- if (num <= 0) {
- OPENSSL_PUT_ERROR(ASN1, ASN1_R_MISSING_SECOND_NUMBER);
- goto err;
- }
- c = *(p++);
- num--;
- for (;;) {
- if (num <= 0)
- break;
- if ((c != '.') && (c != ' ')) {
- OPENSSL_PUT_ERROR(ASN1, ASN1_R_INVALID_SEPARATOR);
- goto err;
- }
- l = 0;
- use_bn = 0;
- for (;;) {
- if (num <= 0)
- break;
- num--;
- c = *(p++);
- if ((c == ' ') || (c == '.'))
- break;
- if ((c < '0') || (c > '9')) {
- OPENSSL_PUT_ERROR(ASN1, ASN1_R_INVALID_DIGIT);
- goto err;
- }
- if (!use_bn && l >= ((ULONG_MAX - 80) / 10L)) {
- use_bn = 1;
- if (!bl)
- bl = BN_new();
- if (!bl || !BN_set_word(bl, l))
- goto err;
- }
- if (use_bn) {
- if (!BN_mul_word(bl, 10L)
- || !BN_add_word(bl, c - '0'))
- goto err;
- } else
- l = l * 10L + (long)(c - '0');
- }
- if (len == 0) {
- if ((first < 2) && (l >= 40)) {
- OPENSSL_PUT_ERROR(ASN1, ASN1_R_SECOND_NUMBER_TOO_LARGE);
- goto err;
- }
- if (use_bn) {
- if (!BN_add_word(bl, first * 40))
- goto err;
- } else
- l += (long)first *40;
- }
- i = 0;
- if (use_bn) {
- int blsize;
- blsize = BN_num_bits(bl);
- blsize = (blsize + 6) / 7;
- if (blsize > tmpsize) {
- if (tmp != ftmp)
- OPENSSL_free(tmp);
- tmpsize = blsize + 32;
- tmp = OPENSSL_malloc(tmpsize);
- if (!tmp)
- goto err;
- }
- while (blsize--) {
- BN_ULONG t = BN_div_word(bl, 0x80L);
- if (t == (BN_ULONG)-1)
- goto err;
- tmp[i++] = (unsigned char)t;
- }
- } else {
-
- for (;;) {
- tmp[i++] = (unsigned char)l & 0x7f;
- l >>= 7L;
- if (l == 0L)
- break;
- }
-
- }
- if (out != NULL) {
- if (len + i > olen) {
- OPENSSL_PUT_ERROR(ASN1, ASN1_R_BUFFER_TOO_SMALL);
- goto err;
- }
- while (--i > 0)
- out[len++] = tmp[i] | 0x80;
- out[len++] = tmp[0];
- } else
- len += i;
- }
- if (tmp != ftmp)
- OPENSSL_free(tmp);
- if (bl)
- BN_free(bl);
- return (len);
- err:
- if (tmp != ftmp)
- OPENSSL_free(tmp);
- if (bl)
- BN_free(bl);
- return (0);
-}
-
int i2t_ASN1_OBJECT(char *buf, int buf_len, ASN1_OBJECT *a)
{
return OBJ_obj2txt(buf, buf_len, a, 0);
diff --git a/src/crypto/bio/bio_test.cc b/src/crypto/bio/bio_test.cc
index eb54f7e..8479c8e 100644
--- a/src/crypto/bio/bio_test.cc
+++ b/src/crypto/bio/bio_test.cc
@@ -27,6 +27,7 @@
#if !defined(OPENSSL_WINDOWS)
#include <arpa/inet.h>
+#include <errno.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <string.h>
diff --git a/src/crypto/bytestring/ber.c b/src/crypto/bytestring/ber.c
index bb5e17c..4dc94f6 100644
--- a/src/crypto/bytestring/ber.c
+++ b/src/crypto/bytestring/ber.c
@@ -29,7 +29,10 @@
// is_string_type returns one if |tag| is a string type and zero otherwise. It
// ignores the constructed bit.
static int is_string_type(unsigned tag) {
- switch (tag & ~CBS_ASN1_CONSTRUCTED) {
+ if ((tag & 0xc0) != 0) {
+ return 0;
+ }
+ switch (tag & 0x1f) {
case CBS_ASN1_BITSTRING:
case CBS_ASN1_OCTETSTRING:
case CBS_ASN1_UTF8STRING:
diff --git a/src/crypto/bytestring/bytestring_test.cc b/src/crypto/bytestring/bytestring_test.cc
index 7e3d453..5a6a5c1 100644
--- a/src/crypto/bytestring/bytestring_test.cc
+++ b/src/crypto/bytestring/bytestring_test.cc
@@ -123,27 +123,27 @@
uint64_t value;
CBS_init(&data, kData1, sizeof(kData1));
- EXPECT_FALSE(CBS_peek_asn1_tag(&data, CBS_ASN1_BOOLEAN));
- EXPECT_TRUE(CBS_peek_asn1_tag(&data, CBS_ASN1_SEQUENCE));
+ EXPECT_FALSE(CBS_peek_asn1_tag(&data, 0x1));
+ EXPECT_TRUE(CBS_peek_asn1_tag(&data, 0x30));
- ASSERT_TRUE(CBS_get_asn1(&data, &contents, CBS_ASN1_SEQUENCE));
+ ASSERT_TRUE(CBS_get_asn1(&data, &contents, 0x30));
EXPECT_EQ(Bytes("\x01\x02"), Bytes(CBS_data(&contents), CBS_len(&contents)));
CBS_init(&data, kData2, sizeof(kData2));
// data is truncated
- EXPECT_FALSE(CBS_get_asn1(&data, &contents, CBS_ASN1_SEQUENCE));
+ EXPECT_FALSE(CBS_get_asn1(&data, &contents, 0x30));
CBS_init(&data, kData3, sizeof(kData3));
// zero byte length of length
- EXPECT_FALSE(CBS_get_asn1(&data, &contents, CBS_ASN1_SEQUENCE));
+ EXPECT_FALSE(CBS_get_asn1(&data, &contents, 0x30));
CBS_init(&data, kData4, sizeof(kData4));
// long form mistakenly used.
- EXPECT_FALSE(CBS_get_asn1(&data, &contents, CBS_ASN1_SEQUENCE));
+ EXPECT_FALSE(CBS_get_asn1(&data, &contents, 0x30));
CBS_init(&data, kData5, sizeof(kData5));
// length takes too many bytes.
- EXPECT_FALSE(CBS_get_asn1(&data, &contents, CBS_ASN1_SEQUENCE));
+ EXPECT_FALSE(CBS_get_asn1(&data, &contents, 0x30));
CBS_init(&data, kData1, sizeof(kData1));
// wrong tag.
@@ -151,72 +151,56 @@
CBS_init(&data, NULL, 0);
// peek at empty data.
- EXPECT_FALSE(CBS_peek_asn1_tag(&data, CBS_ASN1_SEQUENCE));
+ EXPECT_FALSE(CBS_peek_asn1_tag(&data, 0x30));
CBS_init(&data, NULL, 0);
// optional elements at empty data.
- ASSERT_TRUE(CBS_get_optional_asn1(
- &data, &contents, &present,
- CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0));
+ ASSERT_TRUE(CBS_get_optional_asn1(&data, &contents, &present, 0xa0));
EXPECT_FALSE(present);
- ASSERT_TRUE(CBS_get_optional_asn1_octet_string(
- &data, &contents, &present,
- CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0));
+ ASSERT_TRUE(
+ CBS_get_optional_asn1_octet_string(&data, &contents, &present, 0xa0));
EXPECT_FALSE(present);
EXPECT_EQ(0u, CBS_len(&contents));
- ASSERT_TRUE(CBS_get_optional_asn1_octet_string(
- &data, &contents, NULL,
- CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0));
+ ASSERT_TRUE(CBS_get_optional_asn1_octet_string(&data, &contents, NULL, 0xa0));
EXPECT_EQ(0u, CBS_len(&contents));
- ASSERT_TRUE(CBS_get_optional_asn1_uint64(
- &data, &value, CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0, 42));
+ ASSERT_TRUE(CBS_get_optional_asn1_uint64(&data, &value, 0xa0, 42));
EXPECT_EQ(42u, value);
CBS_init(&data, kData6, sizeof(kData6));
// optional element.
- ASSERT_TRUE(CBS_get_optional_asn1(
- &data, &contents, &present,
- CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0));
+ ASSERT_TRUE(CBS_get_optional_asn1(&data, &contents, &present, 0xa0));
EXPECT_FALSE(present);
- ASSERT_TRUE(CBS_get_optional_asn1(
- &data, &contents, &present,
- CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 1));
+ ASSERT_TRUE(CBS_get_optional_asn1(&data, &contents, &present, 0xa1));
EXPECT_TRUE(present);
EXPECT_EQ(Bytes("\x04\x01\x01"),
Bytes(CBS_data(&contents), CBS_len(&contents)));
CBS_init(&data, kData6, sizeof(kData6));
// optional octet string.
- ASSERT_TRUE(CBS_get_optional_asn1_octet_string(
- &data, &contents, &present,
- CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0));
+ ASSERT_TRUE(
+ CBS_get_optional_asn1_octet_string(&data, &contents, &present, 0xa0));
EXPECT_FALSE(present);
EXPECT_EQ(0u, CBS_len(&contents));
- ASSERT_TRUE(CBS_get_optional_asn1_octet_string(
- &data, &contents, &present,
- CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 1));
+ ASSERT_TRUE(
+ CBS_get_optional_asn1_octet_string(&data, &contents, &present, 0xa1));
EXPECT_TRUE(present);
EXPECT_EQ(Bytes("\x01"), Bytes(CBS_data(&contents), CBS_len(&contents)));
CBS_init(&data, kData7, sizeof(kData7));
// invalid optional octet string.
- EXPECT_FALSE(CBS_get_optional_asn1_octet_string(
- &data, &contents, &present,
- CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 1));
+ EXPECT_FALSE(
+ CBS_get_optional_asn1_octet_string(&data, &contents, &present, 0xa1));
CBS_init(&data, kData8, sizeof(kData8));
// optional integer.
- ASSERT_TRUE(CBS_get_optional_asn1_uint64(
- &data, &value, CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0, 42));
+ ASSERT_TRUE(CBS_get_optional_asn1_uint64(&data, &value, 0xa0, 42));
EXPECT_EQ(42u, value);
- ASSERT_TRUE(CBS_get_optional_asn1_uint64(
- &data, &value, CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 1, 42));
+ ASSERT_TRUE(CBS_get_optional_asn1_uint64(&data, &value, 0xa1, 42));
EXPECT_EQ(1u, value);
CBS_init(&data, kData9, sizeof(kData9));
// invalid optional integer.
- EXPECT_FALSE(CBS_get_optional_asn1_uint64(
- &data, &value, CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 1, 42));
+ EXPECT_FALSE(CBS_get_optional_asn1_uint64(&data, &value, 0xa1, 42));
unsigned tag;
CBS_init(&data, kData1, sizeof(kData1));
@@ -233,54 +217,6 @@
Bytes(CBS_data(&contents), CBS_len(&contents)));
}
-TEST(CBSTest, ParseASN1Tag) {
- const struct {
- bool ok;
- unsigned tag;
- std::vector<uint8_t> in;
- } kTests[] = {
- {true, CBS_ASN1_SEQUENCE, {0x30, 0}},
- {true, CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 4, {0xa4, 0}},
- {true, CBS_ASN1_APPLICATION | 30, {0x5e, 0}},
- {true, CBS_ASN1_APPLICATION | 31, {0x5f, 0x1f, 0}},
- {true, CBS_ASN1_APPLICATION | 32, {0x5f, 0x20, 0}},
- {true,
- CBS_ASN1_PRIVATE | CBS_ASN1_CONSTRUCTED | 0x1fffffff,
- {0xff, 0x81, 0xff, 0xff, 0xff, 0x7f, 0}},
- // Tag number fits in unsigned but not |CBS_ASN1_TAG_NUMBER_MASK|.
- {false, 0, {0xff, 0x82, 0xff, 0xff, 0xff, 0x7f, 0}},
- // Tag number does not fit in unsigned.
- {false, 0, {0xff, 0x90, 0x80, 0x80, 0x80, 0, 0}},
- // Tag number is not minimally-encoded
- {false, 0, {0x5f, 0x80, 0x1f, 0}},
- // Tag number should have used short form.
- {false, 0, {0x5f, 0x80, 0x1e, 0}},
- };
- for (const auto &t : kTests) {
- SCOPED_TRACE(Bytes(t.in));
- unsigned tag;
- CBS cbs, child;
- CBS_init(&cbs, t.in.data(), t.in.size());
- ASSERT_EQ(t.ok, !!CBS_get_any_asn1(&cbs, &child, &tag));
- if (t.ok) {
- EXPECT_EQ(t.tag, tag);
- EXPECT_EQ(0u, CBS_len(&child));
- EXPECT_EQ(0u, CBS_len(&cbs));
-
- CBS_init(&cbs, t.in.data(), t.in.size());
- EXPECT_TRUE(CBS_peek_asn1_tag(&cbs, t.tag));
- EXPECT_FALSE(CBS_peek_asn1_tag(&cbs, t.tag + 1));
-
- EXPECT_TRUE(CBS_get_asn1(&cbs, &child, t.tag));
- EXPECT_EQ(0u, CBS_len(&child));
- EXPECT_EQ(0u, CBS_len(&cbs));
-
- CBS_init(&cbs, t.in.data(), t.in.size());
- EXPECT_FALSE(CBS_get_asn1(&cbs, &child, t.tag + 1));
- }
- }
-}
-
TEST(CBSTest, GetOptionalASN1Bool) {
static const uint8_t kTrue[] = {0x0a, 3, CBS_ASN1_BOOLEAN, 1, 0xff};
static const uint8_t kFalse[] = {0x0a, 3, CBS_ASN1_BOOLEAN, 1, 0x00};
@@ -480,42 +416,15 @@
}
TEST(CBBTest, ASN1) {
- static const uint8_t kExpected[] = {
- // SEQUENCE { 1 2 3 }
- 0x30, 3, 1, 2, 3,
- // [4 CONSTRUCTED] { 4 5 6 }
- 0xa4, 3, 4, 5, 6,
- // [APPLICATION 30 PRIMITIVE] { 7 8 9 }
- 0x5e, 3, 7, 8, 9,
- // [APPLICATION 31 PRIMITIVE] { 10 11 12 }
- 0x5f, 0x1f, 3, 10, 11, 12,
- // [PRIVATE 2^29-1 CONSTRUCTED] { 13 14 15 }
- 0xff, 0x81, 0xff, 0xff, 0xff, 0x7f, 3, 13, 14, 15,
- };
+ static const uint8_t kExpected[] = {0x30, 3, 1, 2, 3};
uint8_t *buf;
size_t buf_len;
bssl::ScopedCBB cbb;
CBB contents, inner_contents;
ASSERT_TRUE(CBB_init(cbb.get(), 0));
- ASSERT_TRUE(CBB_add_asn1(cbb.get(), &contents, CBS_ASN1_SEQUENCE));
+ ASSERT_TRUE(CBB_add_asn1(cbb.get(), &contents, 0x30));
ASSERT_TRUE(CBB_add_bytes(&contents, (const uint8_t *)"\x01\x02\x03", 3));
- ASSERT_TRUE(
- CBB_add_asn1(cbb.get(), &contents,
- CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 4));
- ASSERT_TRUE(CBB_add_bytes(&contents, (const uint8_t *)"\x04\x05\x06", 3));
- ASSERT_TRUE(
- CBB_add_asn1(cbb.get(), &contents,
- CBS_ASN1_APPLICATION | 30));
- ASSERT_TRUE(CBB_add_bytes(&contents, (const uint8_t *)"\x07\x08\x09", 3));
- ASSERT_TRUE(
- CBB_add_asn1(cbb.get(), &contents,
- CBS_ASN1_APPLICATION | 31));
- ASSERT_TRUE(CBB_add_bytes(&contents, (const uint8_t *)"\x0a\x0b\x0c", 3));
- ASSERT_TRUE(
- CBB_add_asn1(cbb.get(), &contents,
- CBS_ASN1_PRIVATE | CBS_ASN1_CONSTRUCTED | 0x1fffffff));
- ASSERT_TRUE(CBB_add_bytes(&contents, (const uint8_t *)"\x0d\x0e\x0f", 3));
ASSERT_TRUE(CBB_finish(cbb.get(), &buf, &buf_len));
bssl::UniquePtr<uint8_t> scoper(buf);
@@ -523,7 +432,7 @@
std::vector<uint8_t> test_data(100000, 0x42);
ASSERT_TRUE(CBB_init(cbb.get(), 0));
- ASSERT_TRUE(CBB_add_asn1(cbb.get(), &contents, CBS_ASN1_SEQUENCE));
+ ASSERT_TRUE(CBB_add_asn1(cbb.get(), &contents, 0x30));
ASSERT_TRUE(CBB_add_bytes(&contents, test_data.data(), 130));
ASSERT_TRUE(CBB_finish(cbb.get(), &buf, &buf_len));
scoper.reset(buf);
@@ -533,7 +442,7 @@
EXPECT_EQ(Bytes(test_data.data(), 130), Bytes(buf + 3, 130));
ASSERT_TRUE(CBB_init(cbb.get(), 0));
- ASSERT_TRUE(CBB_add_asn1(cbb.get(), &contents, CBS_ASN1_SEQUENCE));
+ ASSERT_TRUE(CBB_add_asn1(cbb.get(), &contents, 0x30));
ASSERT_TRUE(CBB_add_bytes(&contents, test_data.data(), 1000));
ASSERT_TRUE(CBB_finish(cbb.get(), &buf, &buf_len));
scoper.reset(buf);
@@ -543,8 +452,8 @@
EXPECT_EQ(Bytes(test_data.data(), 1000), Bytes(buf + 4, 1000));
ASSERT_TRUE(CBB_init(cbb.get(), 0));
- ASSERT_TRUE(CBB_add_asn1(cbb.get(), &contents, CBS_ASN1_SEQUENCE));
- ASSERT_TRUE(CBB_add_asn1(&contents, &inner_contents, CBS_ASN1_SEQUENCE));
+ ASSERT_TRUE(CBB_add_asn1(cbb.get(), &contents, 0x30));
+ ASSERT_TRUE(CBB_add_asn1(&contents, &inner_contents, 0x30));
ASSERT_TRUE(CBB_add_bytes(&inner_contents, test_data.data(), 100000));
ASSERT_TRUE(CBB_finish(cbb.get(), &buf, &buf_len));
scoper.reset(buf);
@@ -581,12 +490,6 @@
static const uint8_t kIndefBER[] = {0x30, 0x80, 0x01, 0x01, 0x02, 0x00, 0x00};
static const uint8_t kIndefDER[] = {0x30, 0x03, 0x01, 0x01, 0x02};
- // kIndefBER2 contains a constructed [APPLICATION 31] with an indefinite
- // length.
- static const uint8_t kIndefBER2[] = {0x7f, 0x1f, 0x80, 0x01,
- 0x01, 0x02, 0x00, 0x00};
- static const uint8_t kIndefDER2[] = {0x7f, 0x1f, 0x03, 0x01, 0x01, 0x02};
-
// kOctetStringBER contains an indefinite length OCTET STRING with two parts.
// These parts need to be concatenated in DER form.
static const uint8_t kOctetStringBER[] = {0x24, 0x80, 0x04, 0x02, 0, 1,
@@ -631,8 +534,6 @@
sizeof(kSimpleBER));
ExpectBerConvert("kIndefBER", kIndefDER, sizeof(kIndefDER), kIndefBER,
sizeof(kIndefBER));
- ExpectBerConvert("kIndefBER2", kIndefDER2, sizeof(kIndefDER2), kIndefBER2,
- sizeof(kIndefBER2));
ExpectBerConvert("kOctetStringBER", kOctetStringDER, sizeof(kOctetStringDER),
kOctetStringBER, sizeof(kOctetStringBER));
ExpectBerConvert("kNSSBER", kNSSDER, sizeof(kNSSDER), kNSSBER,
@@ -886,3 +787,98 @@
CBS_asn1_bitstring_has_bit(&cbs, test.bit));
}
}
+
+TEST(CBBTest, AddOIDFromText) {
+ const struct {
+ const char *text;
+ std::vector<uint8_t> der;
+ } kValidOIDs[] = {
+ // Some valid values.
+ {"0.0", {0x00}},
+ {"0.2.3.4", {0x2, 0x3, 0x4}},
+ {"1.2.3.4", {0x2a, 0x3, 0x4}},
+ {"2.2.3.4", {0x52, 0x3, 0x4}},
+ {"1.2.840.113554.4.1.72585",
+ {0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09}},
+ // Test edge cases around the first component.
+ {"0.39", {0x27}},
+ {"1.0", {0x28}},
+ {"1.39", {0x4f}},
+ {"2.0", {0x50}},
+ {"2.1", {0x51}},
+ {"2.40", {0x78}},
+ // Edge cases near an overflow.
+ {"1.2.18446744073709551615",
+ {0x2a, 0x81, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f}},
+ {"2.18446744073709551535",
+ {0x81, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f}},
+ };
+
+ const char *kInvalidTexts[] = {
+ // Invalid second component.
+ "0.40",
+ "1.40",
+ // Invalid first component.
+ "3.1",
+ // The empty string is not an OID.
+ "",
+ // No empty components.
+ ".1.2.3.4.5",
+ "1..2.3.4.5",
+ "1.2.3.4.5.",
+ // There must be at least two components.
+ "1",
+ // No extra leading zeros.
+ "00.1.2.3.4",
+ "01.1.2.3.4",
+ // Overflow for both components or 40*A + B.
+ "1.2.18446744073709551616",
+ "2.18446744073709551536",
+ };
+
+ const std::vector<uint8_t> kInvalidDER[] = {
+ // The empty string is not an OID.
+ {},
+ // Non-minimal representation.
+ {0x80, 0x01},
+ // Overflow. This is the DER representation of
+ // 1.2.840.113554.4.1.72585.18446744073709551616. (The final value is
+ // 2^64.)
+ {0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09,
+ 0x82, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00},
+ };
+
+ for (const auto &t : kValidOIDs) {
+ SCOPED_TRACE(t.text);
+
+ bssl::ScopedCBB cbb;
+ ASSERT_TRUE(CBB_init(cbb.get(), 0));
+ ASSERT_TRUE(CBB_add_asn1_oid_from_text(cbb.get(), t.text, strlen(t.text)));
+ uint8_t *out;
+ size_t len;
+ ASSERT_TRUE(CBB_finish(cbb.get(), &out, &len));
+ bssl::UniquePtr<uint8_t> free_out(out);
+ EXPECT_EQ(Bytes(t.der), Bytes(out, len));
+
+ CBS cbs;
+ CBS_init(&cbs, t.der.data(), t.der.size());
+ bssl::UniquePtr<char> text(CBS_asn1_oid_to_text(&cbs));
+ ASSERT_TRUE(text.get());
+ EXPECT_STREQ(t.text, text.get());
+ }
+
+ for (const char *t : kInvalidTexts) {
+ SCOPED_TRACE(t);
+ bssl::ScopedCBB cbb;
+ ASSERT_TRUE(CBB_init(cbb.get(), 0));
+ EXPECT_FALSE(CBB_add_asn1_oid_from_text(cbb.get(), t, strlen(t)));
+ }
+
+ for (const auto &t : kInvalidDER) {
+ SCOPED_TRACE(Bytes(t));
+ CBS cbs;
+ CBS_init(&cbs, t.data(), t.size());
+ bssl::UniquePtr<char> text(CBS_asn1_oid_to_text(&cbs));
+ EXPECT_FALSE(text);
+ }
+}
diff --git a/src/crypto/bytestring/cbb.c b/src/crypto/bytestring/cbb.c
index b1afe7d..f8f5e0f 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 @@
return cbb_add_length_prefixed(cbb, out_contents, 3);
}
+// 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;
+ }
+ 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;
+}
+
int CBB_add_asn1(CBB *cbb, CBB *out_contents, unsigned tag) {
- if (!CBB_flush(cbb)) {
+ 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;
}
- // 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)) {
- return 0;
- }
-
- 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)) {
+ if (!CBB_flush(cbb) ||
+ // |tag|'s representation matches the DER encoding.
+ !CBB_add_u8(cbb, (uint8_t)tag)) {
return 0;
}
@@ -492,3 +500,69 @@
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;
+}
diff --git a/src/crypto/bytestring/cbs.c b/src/crypto/bytestring/cbs.c
index d96371c..f3fc863 100644
--- a/src/crypto/bytestring/cbs.c
+++ b/src/crypto/bytestring/cbs.c
@@ -12,11 +12,16 @@
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
+#if !defined(__STDC_FORMAT_MACROS)
+#define __STDC_FORMAT_MACROS
+#endif
+
#include <openssl/buf.h>
#include <openssl/mem.h>
#include <openssl/bytestring.h>
#include <assert.h>
+#include <inttypes.h>
#include <string.h>
#include "internal.h"
@@ -175,9 +180,45 @@
return cbs_get_length_prefixed(cbs, out, 3);
}
-static int parse_asn1_tag(CBS *cbs, unsigned *out) {
- uint8_t tag_byte;
- if (!CBS_get_u8(cbs, &tag_byte)) {
+// parse_base128_integer reads a big-endian base-128 integer from |cbs| and sets
+// |*out| to the result. This is the encoding used in DER for both high tag
+// number form and OID components.
+static int parse_base128_integer(CBS *cbs, uint64_t *out) {
+ uint64_t v = 0;
+ uint8_t b;
+ do {
+ if (!CBS_get_u8(cbs, &b)) {
+ return 0;
+ }
+ if ((v >> (64 - 7)) != 0) {
+ // The value is too large.
+ return 0;
+ }
+ if (v == 0 && b == 0x80) {
+ // The value must be minimally encoded.
+ return 0;
+ }
+ v = (v << 7) | (b & 0x7f);
+
+ // Values end at an octet with the high bit cleared.
+ } while (b & 0x80);
+
+ *out = v;
+ return 1;
+}
+
+static int cbs_get_any_asn1_element(CBS *cbs, CBS *out, unsigned *out_tag,
+ size_t *out_header_len, int ber_ok) {
+ 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;
}
@@ -188,70 +229,22 @@
// allotted bits), then the tag is more than one byte long and the
// continuation bytes contain the tag number. This parser only supports tag
// numbers less than 31 (and thus single-byte tags).
- unsigned tag = ((unsigned)tag_byte & 0xe0) << CBS_ASN1_TAG_SHIFT;
- unsigned tag_number = tag_byte & 0x1f;
- if (tag_number == 0x1f) {
- tag_number = 0;
- for (;;) {
- if (!CBS_get_u8(cbs, &tag_byte) ||
- ((tag_number << 7) >> 7) != tag_number) {
- return 0;
- }
- tag_number = (tag_number << 7) | (tag_byte & 0x7f);
- // The tag must be represented in the minimal number of bytes.
- if (tag_number == 0) {
- return 0;
- }
- if ((tag_byte & 0x80) == 0) {
- break;
- }
- }
- if (// Check the tag number is within our supported bounds.
- tag_number > CBS_ASN1_TAG_NUMBER_MASK ||
- // Small tag numbers should have used low tag number form.
- tag_number < 0x1f) {
- return 0;
- }
- }
-
- tag |= tag_number;
-
- *out = tag;
- return 1;
-}
-
-static int cbs_get_any_asn1_element(CBS *cbs, CBS *out, unsigned *out_tag,
- size_t *out_header_len, int ber_ok) {
- CBS header = *cbs;
- CBS throwaway;
-
- if (out == NULL) {
- out = &throwaway;
- }
-
- unsigned tag;
- if (!parse_asn1_tag(&header, &tag)) {
+ if ((tag & 0x1f) == 0x1f) {
return 0;
}
+
if (out_tag != NULL) {
*out_tag = tag;
}
- uint8_t length_byte;
- if (!CBS_get_u8(&header, &length_byte)) {
- return 0;
- }
-
- size_t header_len = CBS_len(cbs) - CBS_len(&header);
-
size_t len;
// The format for the length encoding is specified in ITU-T X.690 section
// 8.1.3.
if ((length_byte & 0x80) == 0) {
// Short form length.
- len = ((size_t) length_byte) + header_len;
+ len = ((size_t) length_byte) + 2;
if (out_header_len != NULL) {
- *out_header_len = header_len;
+ *out_header_len = 2;
}
} else {
// The high bit indicate that this is the long form, while the next 7 bits
@@ -263,9 +256,9 @@
if (ber_ok && (tag & CBS_ASN1_CONSTRUCTED) != 0 && num_bytes == 0) {
// indefinite length
if (out_header_len != NULL) {
- *out_header_len = header_len;
+ *out_header_len = 2;
}
- return CBS_get_bytes(cbs, out, header_len);
+ return CBS_get_bytes(cbs, out, 2);
}
// ITU-T X.690 clause 8.1.3.5.c specifies that the value 0xff shall not be
@@ -288,13 +281,13 @@
return 0;
}
len = len32;
- if (len + header_len + num_bytes < len) {
+ if (len + 2 + num_bytes < len) {
// Overflow.
return 0;
}
- len += header_len + num_bytes;
+ len += 2 + num_bytes;
if (out_header_len != NULL) {
- *out_header_len = header_len + num_bytes;
+ *out_header_len = 2 + num_bytes;
}
}
@@ -362,10 +355,7 @@
if (CBS_len(cbs) < 1) {
return 0;
}
-
- CBS copy = *cbs;
- unsigned actual_tag;
- return parse_asn1_tag(©, &actual_tag) && tag_value == actual_tag;
+ return CBS_data(cbs)[0] == tag_value;
}
int CBS_get_asn1_uint64(CBS *cbs, uint64_t *out) {
@@ -527,3 +517,55 @@
return byte_num < CBS_len(cbs) &&
(CBS_data(cbs)[byte_num] & (1 << bit_num)) != 0;
}
+
+static int add_decimal(CBB *out, uint64_t v) {
+ char buf[DECIMAL_SIZE(uint64_t) + 1];
+ BIO_snprintf(buf, sizeof(buf), "%" PRIu64, v);
+ return CBB_add_bytes(out, (const uint8_t *)buf, strlen(buf));
+}
+
+char *CBS_asn1_oid_to_text(const CBS *cbs) {
+ CBB cbb;
+ if (!CBB_init(&cbb, 32)) {
+ goto err;
+ }
+
+ CBS copy = *cbs;
+ // The first component is 40 * value1 + value2, where value1 is 0, 1, or 2.
+ uint64_t v;
+ if (!parse_base128_integer(©, &v)) {
+ goto err;
+ }
+
+ if (v >= 80) {
+ if (!CBB_add_bytes(&cbb, (const uint8_t *)"2.", 2) ||
+ !add_decimal(&cbb, v - 80)) {
+ goto err;
+ }
+ } else if (!add_decimal(&cbb, v / 40) ||
+ !CBB_add_u8(&cbb, '.') ||
+ !add_decimal(&cbb, v % 40)) {
+ goto err;
+ }
+
+ while (CBS_len(©) != 0) {
+ if (!parse_base128_integer(©, &v) ||
+ !CBB_add_u8(&cbb, '.') ||
+ !add_decimal(&cbb, v)) {
+ goto err;
+ }
+ }
+
+ uint8_t *txt;
+ size_t txt_len;
+ if (!CBB_add_u8(&cbb, '\0') ||
+ !CBB_finish(&cbb, &txt, &txt_len)) {
+ goto err;
+ }
+
+ return (char *)txt;
+
+err:
+ CBB_cleanup(&cbb);
+ return NULL;
+}
diff --git a/src/crypto/err/obj.errordata b/src/crypto/err/obj.errordata
index c54435e..be13451 100644
--- a/src/crypto/err/obj.errordata
+++ b/src/crypto/err/obj.errordata
@@ -1 +1,2 @@
+OBJ,101,INVALID_OID_STRING
OBJ,100,UNKNOWN_NID
diff --git a/src/crypto/fipsmodule/bn/rsaz_exp.c b/src/crypto/fipsmodule/bn/rsaz_exp.c
index d0090a6..cb9a233 100644
--- a/src/crypto/fipsmodule/bn/rsaz_exp.c
+++ b/src/crypto/fipsmodule/bn/rsaz_exp.c
@@ -227,7 +227,9 @@
rsaz_1024_sqr_avx2(result, result, m, k0, 5);
- wvalue = *((const unsigned short*)&p_str[index / 8]);
+ uint16_t wvalue_16;
+ memcpy(&wvalue_16, &p_str[index / 8], sizeof(wvalue_16));
+ wvalue = wvalue_16;
wvalue = (wvalue>> (index%8)) & 31;
index-=5;
diff --git a/src/crypto/fipsmodule/ec/ec.c b/src/crypto/fipsmodule/ec/ec.c
index 977cd26..266baa2 100644
--- a/src/crypto/fipsmodule/ec/ec.c
+++ b/src/crypto/fipsmodule/ec/ec.c
@@ -817,6 +817,24 @@
return ec_GFp_simple_invert(group, a, ctx);
}
+static int arbitrary_bignum_to_scalar(const EC_GROUP *group, EC_SCALAR *out,
+ const BIGNUM *in, BN_CTX *ctx) {
+ const BIGNUM *order = EC_GROUP_get0_order(group);
+ if (BN_is_negative(in) || BN_num_bits(in) > BN_num_bits(order)) {
+ // This is an unusual input, so we do not guarantee constant-time
+ // processing, even ignoring |bn_correct_top|.
+ BN_CTX_start(ctx);
+ BIGNUM *tmp = BN_CTX_get(ctx);
+ int ok = tmp != NULL &&
+ BN_nnmod(tmp, in, order, ctx) &&
+ ec_bignum_to_scalar(group, out, tmp);
+ BN_CTX_end(ctx);
+ return ok;
+ }
+
+ return ec_bignum_to_scalar(group, out, in);
+}
+
int EC_POINT_mul(const EC_GROUP *group, EC_POINT *r, const BIGNUM *g_scalar,
const EC_POINT *p, const BIGNUM *p_scalar, BN_CTX *ctx) {
// Previously, this function set |r| to the point at infinity if there was
@@ -828,30 +846,27 @@
return 0;
}
- // We cannot easily process arbitrary scalars in constant-time, and there is
- // no need to do so. Require that scalars be the same size as the order.
- //
- // One could require they be fully reduced, but some consumers try to check
- // that |order| * |pubkey| is the identity. This comes from following NIST SP
- // 800-56A section 5.6.2.3.2. (Though all our curves have cofactor one, so
- // this check isn't useful.)
int ret = 0;
EC_SCALAR g_scalar_storage, p_scalar_storage;
EC_SCALAR *g_scalar_arg = NULL, *p_scalar_arg = NULL;
- unsigned order_bits = BN_num_bits(&group->order);
+ BN_CTX *new_ctx = NULL;
+ if (ctx == NULL) {
+ new_ctx = BN_CTX_new();
+ if (new_ctx == NULL) {
+ goto err;
+ }
+ ctx = new_ctx;
+ }
+
if (g_scalar != NULL) {
- if (BN_is_negative(g_scalar) || BN_num_bits(g_scalar) > order_bits ||
- !ec_bignum_to_scalar(group, &g_scalar_storage, g_scalar)) {
- OPENSSL_PUT_ERROR(EC, EC_R_INVALID_SCALAR);
+ if (!arbitrary_bignum_to_scalar(group, &g_scalar_storage, g_scalar, ctx)) {
goto err;
}
g_scalar_arg = &g_scalar_storage;
}
if (p_scalar != NULL) {
- if (BN_is_negative(p_scalar) || BN_num_bits(p_scalar) > order_bits ||
- !ec_bignum_to_scalar(group, &p_scalar_storage, p_scalar)) {
- OPENSSL_PUT_ERROR(EC, EC_R_INVALID_SCALAR);
+ if (!arbitrary_bignum_to_scalar(group, &p_scalar_storage, p_scalar, ctx)) {
goto err;
}
p_scalar_arg = &p_scalar_storage;
@@ -860,6 +875,7 @@
ret = ec_point_mul_scalar(group, r, g_scalar_arg, p, p_scalar_arg, ctx);
err:
+ BN_CTX_free(new_ctx);
OPENSSL_cleanse(&g_scalar_storage, sizeof(g_scalar_storage));
OPENSSL_cleanse(&p_scalar_storage, sizeof(p_scalar_storage));
return ret;
diff --git a/src/crypto/fipsmodule/ec/ec_test.cc b/src/crypto/fipsmodule/ec/ec_test.cc
index 5e5ce94..139840e 100644
--- a/src/crypto/fipsmodule/ec/ec_test.cc
+++ b/src/crypto/fipsmodule/ec/ec_test.cc
@@ -439,6 +439,52 @@
<< "p * order did not return point at infinity.";
}
+// Test that |EC_POINT_mul| works with out-of-range scalars. Even beyond the
+// usual |bn_correct_top| disclaimer, we completely disclaim all hope here as a
+// reduction is needed, but we'll compute the right answer.
+TEST_P(ECCurveTest, MulOutOfRange) {
+ bssl::UniquePtr<EC_GROUP> group(EC_GROUP_new_by_curve_name(GetParam().nid));
+ ASSERT_TRUE(group);
+
+ bssl::UniquePtr<BIGNUM> n_minus_one(BN_dup(EC_GROUP_get0_order(group.get())));
+ ASSERT_TRUE(n_minus_one);
+ ASSERT_TRUE(BN_sub_word(n_minus_one.get(), 1));
+
+ bssl::UniquePtr<BIGNUM> minus_one(BN_new());
+ ASSERT_TRUE(minus_one);
+ ASSERT_TRUE(BN_one(minus_one.get()));
+ BN_set_negative(minus_one.get(), 1);
+
+ bssl::UniquePtr<BIGNUM> seven(BN_new());
+ ASSERT_TRUE(seven);
+ ASSERT_TRUE(BN_set_word(seven.get(), 7));
+
+ bssl::UniquePtr<BIGNUM> ten_n_plus_seven(
+ BN_dup(EC_GROUP_get0_order(group.get())));
+ ASSERT_TRUE(ten_n_plus_seven);
+ ASSERT_TRUE(BN_mul_word(ten_n_plus_seven.get(), 10));
+ ASSERT_TRUE(BN_add_word(ten_n_plus_seven.get(), 7));
+
+ bssl::UniquePtr<EC_POINT> point1(EC_POINT_new(group.get())),
+ point2(EC_POINT_new(group.get()));
+ ASSERT_TRUE(point1);
+ ASSERT_TRUE(point2);
+
+ ASSERT_TRUE(EC_POINT_mul(group.get(), point1.get(), n_minus_one.get(),
+ nullptr, nullptr, nullptr));
+ ASSERT_TRUE(EC_POINT_mul(group.get(), point2.get(), minus_one.get(), nullptr,
+ nullptr, nullptr));
+ EXPECT_EQ(0, EC_POINT_cmp(group.get(), point1.get(), point2.get(), nullptr))
+ << "-1 * G and (n-1) * G did not give the same result";
+
+ ASSERT_TRUE(EC_POINT_mul(group.get(), point1.get(), seven.get(), nullptr,
+ nullptr, nullptr));
+ ASSERT_TRUE(EC_POINT_mul(group.get(), point2.get(), ten_n_plus_seven.get(),
+ nullptr, nullptr, nullptr));
+ EXPECT_EQ(0, EC_POINT_cmp(group.get(), point1.get(), point2.get(), nullptr))
+ << "7 * G and (10n + 7) * G did not give the same result";
+}
+
// Test that 10×∞ + G = G.
TEST_P(ECCurveTest, Mul) {
bssl::UniquePtr<EC_GROUP> group(EC_GROUP_new_by_curve_name(GetParam().nid));
diff --git a/src/crypto/obj/obj.c b/src/crypto/obj/obj.c
index 52e265b..a34d6dc 100644
--- a/src/crypto/obj/obj.c
+++ b/src/crypto/obj/obj.c
@@ -389,16 +389,30 @@
return obj->ln;
}
-ASN1_OBJECT *OBJ_txt2obj(const char *s, int dont_search_names) {
- int nid = NID_undef;
- ASN1_OBJECT *op = NULL;
- unsigned char *buf;
- unsigned char *p;
- const unsigned char *bufp;
- int contents_len, total_len;
+static ASN1_OBJECT *create_object_with_text_oid(int (*get_nid)(void),
+ const char *oid,
+ const char *short_name,
+ const char *long_name) {
+ uint8_t *buf;
+ size_t len;
+ CBB cbb;
+ if (!CBB_init(&cbb, 32) ||
+ !CBB_add_asn1_oid_from_text(&cbb, oid, strlen(oid)) ||
+ !CBB_finish(&cbb, &buf, &len)) {
+ OPENSSL_PUT_ERROR(OBJ, OBJ_R_INVALID_OID_STRING);
+ CBB_cleanup(&cbb);
+ return NULL;
+ }
+ ASN1_OBJECT *ret = ASN1_OBJECT_create(get_nid ? get_nid() : NID_undef, buf,
+ len, short_name, long_name);
+ OPENSSL_free(buf);
+ return ret;
+}
+
+ASN1_OBJECT *OBJ_txt2obj(const char *s, int dont_search_names) {
if (!dont_search_names) {
- nid = OBJ_sn2nid(s);
+ int nid = OBJ_sn2nid(s);
if (nid == NID_undef) {
nid = OBJ_ln2nid(s);
}
@@ -408,31 +422,7 @@
}
}
- // Work out size of content octets
- contents_len = a2d_ASN1_OBJECT(NULL, 0, s, -1);
- if (contents_len <= 0) {
- return NULL;
- }
- // Work out total size
- total_len = ASN1_object_size(0, contents_len, V_ASN1_OBJECT);
-
- buf = OPENSSL_malloc(total_len);
- if (buf == NULL) {
- OPENSSL_PUT_ERROR(OBJ, ERR_R_MALLOC_FAILURE);
- return NULL;
- }
-
- p = buf;
- // Write out tag+length
- ASN1_put_object(&p, 0, contents_len, V_ASN1_OBJECT, V_ASN1_UNIVERSAL);
- // Write out contents
- a2d_ASN1_OBJECT(p, contents_len, s, -1);
-
- bufp = buf;
- op = d2i_ASN1_OBJECT(NULL, &bufp, total_len);
- OPENSSL_free(buf);
-
- return op;
+ return create_object_with_text_oid(NULL, s, NULL, NULL);
}
static int strlcpy_int(char *dst, const char *src, int dst_size) {
@@ -444,36 +434,6 @@
return (int)ret;
}
-static int parse_oid_component(CBS *cbs, uint64_t *out) {
- uint64_t v = 0;
- uint8_t b;
- do {
- if (!CBS_get_u8(cbs, &b)) {
- return 0;
- }
- if ((v >> (64 - 7)) != 0) {
- // The component is too large.
- return 0;
- }
- if (v == 0 && b == 0x80) {
- // The component must be minimally encoded.
- return 0;
- }
- v = (v << 7) | (b & 0x7f);
-
- // Components end at an octet with the high bit cleared.
- } while (b & 0x80);
-
- *out = v;
- return 1;
-}
-
-static int add_decimal(CBB *out, uint64_t v) {
- char buf[DECIMAL_SIZE(uint64_t) + 1];
- BIO_snprintf(buf, sizeof(buf), "%" PRIu64, v);
- return CBB_add_bytes(out, (const uint8_t *)buf, strlen(buf));
-}
-
int OBJ_obj2txt(char *out, int out_len, const ASN1_OBJECT *obj,
int always_return_oid) {
// Python depends on the empty OID successfully encoding as the empty
@@ -495,56 +455,19 @@
}
}
- CBB cbb;
- if (!CBB_init(&cbb, 32)) {
- goto err;
- }
-
CBS cbs;
CBS_init(&cbs, obj->data, obj->length);
-
- // The first component is 40 * value1 + value2, where value1 is 0, 1, or 2.
- uint64_t v;
- if (!parse_oid_component(&cbs, &v)) {
- goto err;
- }
-
- if (v >= 80) {
- if (!CBB_add_bytes(&cbb, (const uint8_t *)"2.", 2) ||
- !add_decimal(&cbb, v - 80)) {
- goto err;
+ char *txt = CBS_asn1_oid_to_text(&cbs);
+ if (txt == NULL) {
+ if (out_len > 0) {
+ out[0] = '\0';
}
- } else if (!add_decimal(&cbb, v / 40) ||
- !CBB_add_u8(&cbb, '.') ||
- !add_decimal(&cbb, v % 40)) {
- goto err;
+ return -1;
}
- while (CBS_len(&cbs) != 0) {
- if (!parse_oid_component(&cbs, &v) ||
- !CBB_add_u8(&cbb, '.') ||
- !add_decimal(&cbb, v)) {
- goto err;
- }
- }
-
- uint8_t *txt;
- size_t txt_len;
- if (!CBB_add_u8(&cbb, '\0') ||
- !CBB_finish(&cbb, &txt, &txt_len)) {
- goto err;
- }
-
- int ret = strlcpy_int(out, (const char *)txt, out_len);
+ int ret = strlcpy_int(out, txt, out_len);
OPENSSL_free(txt);
return ret;
-
-err:
- CBB_cleanup(&cbb);
- if (out_len > 0) {
- out[0] = '\0';
- }
- return -1;
}
static uint32_t hash_nid(const ASN1_OBJECT *obj) {
@@ -621,41 +544,11 @@
}
int OBJ_create(const char *oid, const char *short_name, const char *long_name) {
- int ret = NID_undef;
- ASN1_OBJECT *op = NULL;
- unsigned char *buf = NULL;
- int len;
-
- len = a2d_ASN1_OBJECT(NULL, 0, oid, -1);
- if (len <= 0) {
- goto err;
+ ASN1_OBJECT *op =
+ create_object_with_text_oid(obj_next_nid, oid, short_name, long_name);
+ if (op == NULL ||
+ !obj_add_object(op)) {
+ return NID_undef;
}
-
- buf = OPENSSL_malloc(len);
- if (buf == NULL) {
- OPENSSL_PUT_ERROR(OBJ, ERR_R_MALLOC_FAILURE);
- goto err;
- }
-
- len = a2d_ASN1_OBJECT(buf, len, oid, -1);
- if (len == 0) {
- goto err;
- }
-
- op = (ASN1_OBJECT *)ASN1_OBJECT_create(obj_next_nid(), buf, len, short_name,
- long_name);
- if (op == NULL) {
- goto err;
- }
-
- if (obj_add_object(op)) {
- ret = op->nid;
- }
- op = NULL;
-
-err:
- ASN1_OBJECT_free(op);
- OPENSSL_free(buf);
-
- return ret;
+ return op->nid;
}
diff --git a/src/crypto/x509/x509_test.cc b/src/crypto/x509/x509_test.cc
index cd4e61d..158fd45 100644
--- a/src/crypto/x509/x509_test.cc
+++ b/src/crypto/x509/x509_test.cc
@@ -26,6 +26,7 @@
#include <openssl/pem.h>
#include <openssl/pool.h>
#include <openssl/x509.h>
+#include <openssl/x509v3.h>
#include "../internal.h"
@@ -834,7 +835,7 @@
/* This ensures the X509 took a reference to |buf|, otherwise this will be a
* reference to free memory and ASAN should notice. */
- ASSERT_EQ(0x30, enc_pointer[0]);
+ ASSERT_EQ(CBS_ASN1_SEQUENCE, enc_pointer[0]);
}
TEST(X509Test, TestFromBufferWithTrailingData) {
@@ -996,3 +997,41 @@
std::string(reinterpret_cast<const char *>(contents), len));
}
}
+
+TEST(X509Test, PrettyPrintIntegers) {
+ static const char *kTests[] = {
+ // Small numbers are pretty-printed in decimal.
+ "0",
+ "-1",
+ "1",
+ "42",
+ "-42",
+ "256",
+ "-256",
+ // Large numbers are pretty-printed in hex to avoid taking quadratic time.
+ "0x0123456789",
+ "-0x0123456789",
+ };
+ for (const char *in : kTests) {
+ SCOPED_TRACE(in);
+ BIGNUM *bn = nullptr;
+ ASSERT_TRUE(BN_asc2bn(&bn, in));
+ bssl::UniquePtr<BIGNUM> free_bn(bn);
+
+ {
+ bssl::UniquePtr<ASN1_INTEGER> asn1(BN_to_ASN1_INTEGER(bn, nullptr));
+ ASSERT_TRUE(asn1);
+ bssl::UniquePtr<char> out(i2s_ASN1_INTEGER(nullptr, asn1.get()));
+ ASSERT_TRUE(out.get());
+ EXPECT_STREQ(in, out.get());
+ }
+
+ {
+ bssl::UniquePtr<ASN1_ENUMERATED> asn1(BN_to_ASN1_ENUMERATED(bn, nullptr));
+ ASSERT_TRUE(asn1);
+ bssl::UniquePtr<char> out(i2s_ASN1_ENUMERATED(nullptr, asn1.get()));
+ ASSERT_TRUE(out.get());
+ EXPECT_STREQ(in, out.get());
+ }
+ }
+}
diff --git a/src/crypto/x509v3/v3_utl.c b/src/crypto/x509v3/v3_utl.c
index feb3dc6..7d109ee 100644
--- a/src/crypto/x509v3/v3_utl.c
+++ b/src/crypto/x509v3/v3_utl.c
@@ -155,6 +155,45 @@
return 1;
}
+static char *bignum_to_string(const BIGNUM *bn)
+{
+ char *tmp, *ret;
+ size_t len;
+
+ /*
+ * Display large numbers in hex and small numbers in decimal. Converting to
+ * decimal takes quadratic time and is no more useful than hex for large
+ * numbers.
+ */
+ if (BN_num_bits(bn) < 32) {
+ return BN_bn2dec(bn);
+ }
+
+ tmp = BN_bn2hex(bn);
+ if (tmp == NULL) {
+ return NULL;
+ }
+
+ len = strlen(tmp) + 3;
+ ret = OPENSSL_malloc(len);
+ if (ret == NULL) {
+ OPENSSL_PUT_ERROR(X509V3, ERR_R_MALLOC_FAILURE);
+ OPENSSL_free(tmp);
+ return NULL;
+ }
+
+ /* Prepend "0x", but place it after the "-" if negative. */
+ if (tmp[0] == '-') {
+ BUF_strlcpy(ret, "-0x", len);
+ BUF_strlcat(ret, tmp + 1, len);
+ } else {
+ BUF_strlcpy(ret, "0x", len);
+ BUF_strlcat(ret, tmp, len);
+ }
+ OPENSSL_free(tmp);
+ return ret;
+}
+
char *i2s_ASN1_ENUMERATED(X509V3_EXT_METHOD *method, ASN1_ENUMERATED *a)
{
BIGNUM *bntmp = NULL;
@@ -162,7 +201,7 @@
if (!a)
return NULL;
if (!(bntmp = ASN1_ENUMERATED_to_BN(a, NULL)) ||
- !(strtmp = BN_bn2dec(bntmp)))
+ !(strtmp = bignum_to_string(bntmp)))
OPENSSL_PUT_ERROR(X509V3, ERR_R_MALLOC_FAILURE);
BN_free(bntmp);
return strtmp;
@@ -175,7 +214,7 @@
if (!a)
return NULL;
if (!(bntmp = ASN1_INTEGER_to_BN(a, NULL)) ||
- !(strtmp = BN_bn2dec(bntmp)))
+ !(strtmp = bignum_to_string(bntmp)))
OPENSSL_PUT_ERROR(X509V3, ERR_R_MALLOC_FAILURE);
BN_free(bntmp);
return strtmp;
diff --git a/src/include/openssl/asn1.h b/src/include/openssl/asn1.h
index 2ef4c97..5c8bf4c 100644
--- a/src/include/openssl/asn1.h
+++ b/src/include/openssl/asn1.h
@@ -737,7 +737,6 @@
OPENSSL_EXPORT int i2a_ASN1_STRING(BIO *bp, ASN1_STRING *a, int type);
OPENSSL_EXPORT int i2t_ASN1_OBJECT(char *buf,int buf_len,ASN1_OBJECT *a);
-OPENSSL_EXPORT int a2d_ASN1_OBJECT(unsigned char *out,int olen, const char *buf, int num);
OPENSSL_EXPORT ASN1_OBJECT *ASN1_OBJECT_create(int nid, unsigned char *data,int len, const char *sn, const char *ln);
OPENSSL_EXPORT int ASN1_INTEGER_set(ASN1_INTEGER *a, long v);
diff --git a/src/include/openssl/bytestring.h b/src/include/openssl/bytestring.h
index 3906809..25411c7 100644
--- a/src/include/openssl/bytestring.h
+++ b/src/include/openssl/bytestring.h
@@ -164,36 +164,34 @@
#define CBS_ASN1_UNIVERSALSTRING 0x1cu
#define CBS_ASN1_BMPSTRING 0x1eu
-// CBS_ASN1_TAG_SHIFT is how much the in-memory representation shifts the class
-// and constructed bits from the DER serialization. This allows representing tag
-// numbers beyond 31.
-//
-// Consumers must use the following constants to decompose or assemble tags.
-#define CBS_ASN1_TAG_SHIFT 24
-
// CBS_ASN1_CONSTRUCTED may be ORed into a tag to toggle the constructed
// bit. |CBS| and |CBB| APIs consider the constructed bit to be part of the
// tag.
-#define CBS_ASN1_CONSTRUCTED (0x20u << CBS_ASN1_TAG_SHIFT)
+#define CBS_ASN1_CONSTRUCTED 0x20u
-// The following values specify the tag class and may be ORed into a tag number
-// to produce the final tag. If none is used, the tag will be UNIVERSAL.
-#define CBS_ASN1_UNIVERSAL (0u << CBS_ASN1_TAG_SHIFT)
-#define CBS_ASN1_APPLICATION (0x40u << CBS_ASN1_TAG_SHIFT)
-#define CBS_ASN1_CONTEXT_SPECIFIC (0x80u << CBS_ASN1_TAG_SHIFT)
-#define CBS_ASN1_PRIVATE (0xc0u << CBS_ASN1_TAG_SHIFT)
+// The following values specify the constructed bit or tag class and may be ORed
+// into a tag number to produce the final tag. If none is used, the tag will be
+// UNIVERSAL.
+//
+// Note that although they currently match the DER serialization, consumers must
+// use these bits rather than make assumptions about the representation. This is
+// to allow for tag numbers beyond 31 in the future.
+#define CBS_ASN1_APPLICATION 0x40u
+#define CBS_ASN1_CONTEXT_SPECIFIC 0x80u
+#define CBS_ASN1_PRIVATE 0xc0u
-// CBS_ASN1_CLASS_MASK may be ANDed with a tag to query its class. This will
-// give one of the four values above.
-#define CBS_ASN1_CLASS_MASK (0xc0u << CBS_ASN1_TAG_SHIFT)
+// CBS_ASN1_CLASS_MASK may be ANDed with a tag to query its class.
+#define CBS_ASN1_CLASS_MASK 0xc0u
// CBS_ASN1_TAG_NUMBER_MASK may be ANDed with a tag to query its number.
-#define CBS_ASN1_TAG_NUMBER_MASK ((1u << (5 + CBS_ASN1_TAG_SHIFT)) - 1)
+#define CBS_ASN1_TAG_NUMBER_MASK 0x1fu
// CBS_get_asn1 sets |*out| to the contents of DER-encoded, ASN.1 element (not
// including tag and length bytes) and advances |cbs| over it. The ASN.1
// element must match |tag_value|. It returns one on success and zero
// on error.
+//
+// Tag numbers greater than 30 are not supported (i.e. short form only).
OPENSSL_EXPORT int CBS_get_asn1(CBS *cbs, CBS *out, unsigned tag_value);
// CBS_get_asn1_element acts like |CBS_get_asn1| but |out| will include the
@@ -211,12 +209,16 @@
// (not including tag and length bytes), sets |*out_tag| to the tag number, and
// advances |*cbs|. It returns one on success and zero on error. Either of |out|
// and |out_tag| may be NULL to ignore the value.
+//
+// Tag numbers greater than 30 are not supported (i.e. short form only).
OPENSSL_EXPORT int CBS_get_any_asn1(CBS *cbs, CBS *out, unsigned *out_tag);
// CBS_get_any_asn1_element sets |*out| to contain the next ASN.1 element from
// |*cbs| (including header bytes) and advances |*cbs|. It sets |*out_tag| to
// the tag number and |*out_header_len| to the length of the ASN.1 header. Each
// of |out|, |out_tag|, and |out_header_len| may be NULL to ignore the value.
+//
+// Tag numbers greater than 30 are not supported (i.e. short form only).
OPENSSL_EXPORT int CBS_get_any_asn1_element(CBS *cbs, CBS *out,
unsigned *out_tag,
size_t *out_header_len);
@@ -279,6 +281,13 @@
// is indexed starting from zero.
OPENSSL_EXPORT int CBS_asn1_bitstring_has_bit(const CBS *cbs, unsigned bit);
+// CBS_asn1_oid_to_text interprets |cbs| as DER-encoded ASN.1 OBJECT IDENTIFIER
+// contents (not including the element framing) and returns the ASCII
+// representation (e.g., "1.2.840.113554.4.1.72585") in a newly-allocated
+// string, or NULL on failure. The caller must release the result with
+// |OPENSSL_free|.
+OPENSSL_EXPORT char *CBS_asn1_oid_to_text(const CBS *cbs);
+
// CRYPTO ByteBuilder.
//
@@ -394,7 +403,9 @@
// CBB_add_asn1 sets |*out_contents| to a |CBB| into which the contents of an
// ASN.1 object can be written. The |tag| argument will be used as the tag for
-// the object. It returns one on success or zero on error.
+// the object. Passing in |tag| number 31 will return in an error since only
+// single octet identifiers are supported. It returns one on success or zero
+// on error.
OPENSSL_EXPORT int CBB_add_asn1(CBB *cbb, CBB *out_contents, unsigned tag);
// CBB_add_bytes appends |len| bytes from |data| to |cbb|. It returns one on
@@ -443,6 +454,17 @@
// error.
OPENSSL_EXPORT int CBB_add_asn1_uint64(CBB *cbb, uint64_t value);
+// CBB_add_asn1_oid_from_text decodes |len| bytes from |text| as an ASCII OID
+// representation, e.g. "1.2.840.113554.4.1.72585", and writes the DER-encoded
+// contents to |cbb|. It returns one on success and zero on malloc failure or if
+// |text| was invalid. It does not include the OBJECT IDENTIFER framing, only
+// the element's contents.
+//
+// This function considers OID strings with components which do not fit in a
+// |uint64_t| to be invalid.
+OPENSSL_EXPORT int CBB_add_asn1_oid_from_text(CBB *cbb, const char *text,
+ size_t len);
+
#if defined(__cplusplus)
} // extern C
diff --git a/src/include/openssl/err.h b/src/include/openssl/err.h
index 9a65ffb..920c306 100644
--- a/src/include/openssl/err.h
+++ b/src/include/openssl/err.h
@@ -262,14 +262,6 @@
// ERR_clear_error clears the error queue for the current thread.
OPENSSL_EXPORT void ERR_clear_error(void);
-// ERR_remove_thread_state clears the error queue for the current thread if
-// |tid| is NULL. Otherwise it calls |assert(0)|, because it's no longer
-// possible to delete the error queue for other threads.
-//
-// Error queues are thread-local data and are deleted automatically. You do not
-// need to call this function. Use |ERR_clear_error|.
-OPENSSL_EXPORT void ERR_remove_thread_state(const CRYPTO_THREADID *tid);
-
// ERR_set_mark "marks" the most recent error for use with |ERR_pop_to_mark|.
// It returns one if an error was marked and zero if there are no errors.
OPENSSL_EXPORT int ERR_set_mark(void);
@@ -382,6 +374,14 @@
// ERR_remove_state calls |ERR_clear_error|.
OPENSSL_EXPORT void ERR_remove_state(unsigned long pid);
+// ERR_remove_thread_state clears the error queue for the current thread if
+// |tid| is NULL. Otherwise it calls |assert(0)|, because it's no longer
+// possible to delete the error queue for other threads.
+//
+// Use |ERR_clear_error| instead. Note error queues are deleted automatically on
+// thread exit. You do not need to call this function to release memory.
+OPENSSL_EXPORT void ERR_remove_thread_state(const CRYPTO_THREADID *tid);
+
// ERR_func_error_string returns the string "OPENSSL_internal".
OPENSSL_EXPORT const char *ERR_func_error_string(uint32_t packed_error);
diff --git a/src/include/openssl/obj.h b/src/include/openssl/obj.h
index 2bdc3b7..374658e 100644
--- a/src/include/openssl/obj.h
+++ b/src/include/openssl/obj.h
@@ -228,5 +228,6 @@
#endif
#define OBJ_R_UNKNOWN_NID 100
+#define OBJ_R_INVALID_OID_STRING 101
#endif // OPENSSL_HEADER_OBJ_H
diff --git a/src/include/openssl/ssl.h b/src/include/openssl/ssl.h
index 53a8eb5..cdf68a1 100644
--- a/src/include/openssl/ssl.h
+++ b/src/include/openssl/ssl.h
@@ -593,7 +593,7 @@
#define TLS1_3_DRAFT_VERSION 0x7f12
#define TLS1_3_DRAFT21_VERSION 0x7f15
-#define TLS1_3_DRAFT22_VERSION 0x7e04
+#define TLS1_3_DRAFT22_VERSION 0x7f16
#define TLS1_3_EXPERIMENT_VERSION 0x7e01
#define TLS1_3_EXPERIMENT2_VERSION 0x7e02
#define TLS1_3_EXPERIMENT3_VERSION 0x7e03
diff --git a/src/ssl/handshake_client.cc b/src/ssl/handshake_client.cc
index 583aceb..53adba6 100644
--- a/src/ssl/handshake_client.cc
+++ b/src/ssl/handshake_client.cc
@@ -295,11 +295,6 @@
return 0;
}
- // Renegotiations do not participate in session resumption.
- int has_session_id = ssl->session != NULL &&
- !ssl->s3->initial_handshake_complete &&
- ssl->session->session_id_length > 0;
-
CBB child;
if (!CBB_add_u16(&body, hs->client_version) ||
!CBB_add_bytes(&body, ssl->s3->client_random, SSL3_RANDOM_SIZE) ||
@@ -307,19 +302,10 @@
return 0;
}
- if (has_session_id) {
- if (!CBB_add_bytes(&child, ssl->session->session_id,
- ssl->session->session_id_length)) {
- return 0;
- }
- } else {
- // In TLS 1.3 experimental encodings, send a fake placeholder session ID
- // when we do not otherwise have one to send.
- if (hs->max_version >= TLS1_3_VERSION &&
- ssl_is_resumption_variant(ssl->tls13_variant) &&
- !CBB_add_bytes(&child, hs->session_id, hs->session_id_len)) {
- return 0;
- }
+ // Do not send a session ID on renegotiation.
+ if (!ssl->s3->initial_handshake_complete &&
+ !CBB_add_bytes(&child, hs->session_id, hs->session_id_len)) {
+ return 0;
}
if (SSL_is_dtls(ssl)) {
@@ -472,7 +458,13 @@
// Initialize a random session ID for the experimental TLS 1.3 variant
// requiring a session id.
- if (ssl_is_resumption_variant(ssl->tls13_variant)) {
+ if (ssl->session != nullptr &&
+ !ssl->s3->initial_handshake_complete &&
+ ssl->session->session_id_length > 0) {
+ hs->session_id_len = ssl->session->session_id_length;
+ OPENSSL_memcpy(hs->session_id, ssl->session->session_id,
+ hs->session_id_len);
+ } else if (ssl_is_resumption_variant(ssl->tls13_variant)) {
hs->session_id_len = sizeof(hs->session_id);
if (!RAND_bytes(hs->session_id, hs->session_id_len)) {
return ssl_hs_error;
diff --git a/src/ssl/test/runner/common.go b/src/ssl/test/runner/common.go
index 0216401..5343346 100644
--- a/src/ssl/test/runner/common.go
+++ b/src/ssl/test/runner/common.go
@@ -38,7 +38,7 @@
tls13ExperimentVersion = 0x7e01
tls13Experiment2Version = 0x7e02
tls13Experiment3Version = 0x7e03
- tls13Draft22Version = 0x7e04
+ tls13Draft22Version = 0x7f16
)
const (
diff --git a/src/ssl/test/runner/conn.go b/src/ssl/test/runner/conn.go
index 21f491a..ec320ae 100644
--- a/src/ssl/test/runner/conn.go
+++ b/src/ssl/test/runner/conn.go
@@ -1383,7 +1383,7 @@
}
if !m.unmarshal(data) {
- return nil, c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage))
+ return nil, c.in.setErrorLocked(c.sendAlert(alertDecodeError))
}
return m, nil
}
diff --git a/src/ssl/test/runner/handshake_messages.go b/src/ssl/test/runner/handshake_messages.go
index 93da121..93d02e1 100644
--- a/src/ssl/test/runner/handshake_messages.go
+++ b/src/ssl/test/runner/handshake_messages.go
@@ -125,6 +125,121 @@
bb.child = nil
}
+type byteReader []byte
+
+func (br *byteReader) readInternal(out *byteReader, n int) bool {
+ if len(*br) < n {
+ return false
+ }
+ *out = (*br)[:n]
+ *br = (*br)[n:]
+ return true
+}
+
+func (br *byteReader) readBytes(out *[]byte, n int) bool {
+ var child byteReader
+ if !br.readInternal(&child, n) {
+ return false
+ }
+ *out = []byte(child)
+ return true
+}
+
+func (br *byteReader) readUint(out *uint64, n int) bool {
+ var b []byte
+ if !br.readBytes(&b, n) {
+ return false
+ }
+ *out = 0
+ for _, v := range b {
+ *out <<= 8
+ *out |= uint64(v)
+ }
+ return true
+}
+
+func (br *byteReader) readU8(out *uint8) bool {
+ var b []byte
+ if !br.readBytes(&b, 1) {
+ return false
+ }
+ *out = b[0]
+ return true
+}
+
+func (br *byteReader) readU16(out *uint16) bool {
+ var v uint64
+ if !br.readUint(&v, 2) {
+ return false
+ }
+ *out = uint16(v)
+ return true
+}
+
+func (br *byteReader) readU24(out *uint32) bool {
+ var v uint64
+ if !br.readUint(&v, 3) {
+ return false
+ }
+ *out = uint32(v)
+ return true
+}
+
+func (br *byteReader) readU32(out *uint32) bool {
+ var v uint64
+ if !br.readUint(&v, 4) {
+ return false
+ }
+ *out = uint32(v)
+ return true
+}
+
+func (br *byteReader) readU64(out *uint64) bool {
+ return br.readUint(out, 8)
+}
+
+func (br *byteReader) readLengthPrefixed(out *byteReader, n int) bool {
+ var length uint64
+ return br.readUint(&length, n) &&
+ uint64(len(*br)) >= length &&
+ br.readInternal(out, int(length))
+}
+
+func (br *byteReader) readLengthPrefixedBytes(out *[]byte, n int) bool {
+ var length uint64
+ return br.readUint(&length, n) &&
+ uint64(len(*br)) >= length &&
+ br.readBytes(out, int(length))
+}
+
+func (br *byteReader) readU8LengthPrefixed(out *byteReader) bool {
+ return br.readLengthPrefixed(out, 1)
+}
+func (br *byteReader) readU8LengthPrefixedBytes(out *[]byte) bool {
+ return br.readLengthPrefixedBytes(out, 1)
+}
+
+func (br *byteReader) readU16LengthPrefixed(out *byteReader) bool {
+ return br.readLengthPrefixed(out, 2)
+}
+func (br *byteReader) readU16LengthPrefixedBytes(out *[]byte) bool {
+ return br.readLengthPrefixedBytes(out, 2)
+}
+
+func (br *byteReader) readU24LengthPrefixed(out *byteReader) bool {
+ return br.readLengthPrefixed(out, 3)
+}
+func (br *byteReader) readU24LengthPrefixedBytes(out *[]byte) bool {
+ return br.readLengthPrefixedBytes(out, 3)
+}
+
+func (br *byteReader) readU32LengthPrefixed(out *byteReader) bool {
+ return br.readLengthPrefixed(out, 4)
+}
+func (br *byteReader) readU32LengthPrefixedBytes(out *[]byte) bool {
+ return br.readLengthPrefixedBytes(out, 4)
+}
+
type keyShareEntry struct {
group CurveID
keyExchange []byte
@@ -422,11 +537,7 @@
srtpProtectionProfiles := useSrtpExt.addU16LengthPrefixed()
for _, p := range m.srtpProtectionProfiles {
- // An SRTPProtectionProfile is defined as uint8[2],
- // not uint16. For some reason, we're storing it
- // as a uint16.
- srtpProtectionProfiles.addU8(byte(p >> 8))
- srtpProtectionProfiles.addU8(byte(p))
+ srtpProtectionProfiles.addU16(p)
}
srtpMki := useSrtpExt.addU8LengthPrefixed()
srtpMki.addBytes([]byte(m.srtpMasterKeyIdentifier))
@@ -482,58 +593,54 @@
return m.raw
}
+func parseSignatureAlgorithms(reader *byteReader, out *[]signatureAlgorithm) bool {
+ var sigAlgs byteReader
+ if !reader.readU16LengthPrefixed(&sigAlgs) {
+ return false
+ }
+ *out = make([]signatureAlgorithm, 0, len(sigAlgs)/2)
+ for len(sigAlgs) > 0 {
+ var v uint16
+ if !sigAlgs.readU16(&v) {
+ return false
+ }
+ *out = append(*out, signatureAlgorithm(v))
+ }
+ return true
+}
+
func (m *clientHelloMsg) unmarshal(data []byte) bool {
- if len(data) < 42 {
- return false
- }
m.raw = data
- m.vers = uint16(data[4])<<8 | uint16(data[5])
- m.random = data[6:38]
- sessionIdLen := int(data[38])
- if sessionIdLen > 32 || len(data) < 39+sessionIdLen {
+ reader := byteReader(data[4:])
+ if !reader.readU16(&m.vers) ||
+ !reader.readBytes(&m.random, 32) ||
+ !reader.readU8LengthPrefixedBytes(&m.sessionId) ||
+ len(m.sessionId) > 32 {
return false
}
- m.sessionId = data[39 : 39+sessionIdLen]
- data = data[39+sessionIdLen:]
if m.isDTLS {
- if len(data) < 1 {
+ if !reader.readU8LengthPrefixedBytes(&m.cookie) ||
+ len(m.cookie) > 32 {
return false
}
- cookieLen := int(data[0])
- if cookieLen > 32 || len(data) < 1+cookieLen {
+ }
+ var cipherSuites byteReader
+ if !reader.readU16LengthPrefixed(&cipherSuites) ||
+ !reader.readU8LengthPrefixedBytes(&m.compressionMethods) {
+ return false
+ }
+
+ m.cipherSuites = make([]uint16, 0, len(cipherSuites)/2)
+ for len(cipherSuites) > 0 {
+ var v uint16
+ if !cipherSuites.readU16(&v) {
return false
}
- m.cookie = data[1 : 1+cookieLen]
- data = data[1+cookieLen:]
- }
- if len(data) < 2 {
- return false
- }
- // cipherSuiteLen is the number of bytes of cipher suite numbers. Since
- // they are uint16s, the number must be even.
- cipherSuiteLen := int(data[0])<<8 | int(data[1])
- if cipherSuiteLen%2 == 1 || len(data) < 2+cipherSuiteLen {
- return false
- }
- numCipherSuites := cipherSuiteLen / 2
- m.cipherSuites = make([]uint16, numCipherSuites)
- for i := 0; i < numCipherSuites; i++ {
- m.cipherSuites[i] = uint16(data[2+2*i])<<8 | uint16(data[3+2*i])
- if m.cipherSuites[i] == scsvRenegotiation {
+ m.cipherSuites = append(m.cipherSuites, v)
+ if v == scsvRenegotiation {
m.secureRenegotiation = []byte{}
}
}
- data = data[2+cipherSuiteLen:]
- if len(data) < 1 {
- return false
- }
- compressionMethodsLen := int(data[0])
- if len(data) < 1+compressionMethodsLen {
- return false
- }
- m.compressionMethods = data[1 : 1+compressionMethodsLen]
-
- data = data[1+compressionMethodsLen:]
m.nextProtoNeg = false
m.serverName = ""
@@ -549,167 +656,108 @@
m.extendedMasterSecret = false
m.customExtension = ""
- if len(data) == 0 {
+ if len(reader) == 0 {
// ClientHello is optionally followed by extension data
return true
}
- if len(data) < 2 {
+
+ var extensions byteReader
+ if !reader.readU16LengthPrefixed(&extensions) || len(reader) != 0 {
return false
}
-
- extensionsLength := int(data[0])<<8 | int(data[1])
- data = data[2:]
- if extensionsLength != len(data) {
- return false
- }
-
- for len(data) != 0 {
- if len(data) < 4 {
+ for len(extensions) > 0 {
+ var extension uint16
+ var body byteReader
+ if !extensions.readU16(&extension) ||
+ !extensions.readU16LengthPrefixed(&body) {
return false
}
- extension := uint16(data[0])<<8 | uint16(data[1])
- length := int(data[2])<<8 | int(data[3])
- data = data[4:]
- if len(data) < length {
- return false
- }
-
switch extension {
case extensionServerName:
- if length < 2 {
+ var names byteReader
+ if !body.readU16LengthPrefixed(&names) || len(body) != 0 {
return false
}
- numNames := int(data[0])<<8 | int(data[1])
- d := data[2:]
- for i := 0; i < numNames; i++ {
- if len(d) < 3 {
- return false
- }
- nameType := d[0]
- nameLen := int(d[1])<<8 | int(d[2])
- d = d[3:]
- if len(d) < nameLen {
+ for len(names) > 0 {
+ var nameType byte
+ var name []byte
+ if !names.readU8(&nameType) ||
+ !names.readU16LengthPrefixedBytes(&name) {
return false
}
if nameType == 0 {
- m.serverName = string(d[0:nameLen])
- break
+ m.serverName = string(name)
}
- d = d[nameLen:]
}
case extensionNextProtoNeg:
- if length > 0 {
+ if len(body) != 0 {
return false
}
m.nextProtoNeg = true
case extensionStatusRequest:
- m.ocspStapling = length > 0 && data[0] == statusTypeOCSP
+ m.ocspStapling = len(body) > 0 && body[0] == statusTypeOCSP
case extensionSupportedCurves:
// http://tools.ietf.org/html/rfc4492#section-5.5.1
- if length < 2 {
+ var curves byteReader
+ if !body.readU16LengthPrefixed(&curves) || len(body) != 0 {
return false
}
- l := int(data[0])<<8 | int(data[1])
- if l%2 == 1 || length != l+2 {
- return false
- }
- numCurves := l / 2
- m.supportedCurves = make([]CurveID, numCurves)
- d := data[2:]
- for i := 0; i < numCurves; i++ {
- m.supportedCurves[i] = CurveID(d[0])<<8 | CurveID(d[1])
- d = d[2:]
+ m.supportedCurves = make([]CurveID, 0, len(curves)/2)
+ for len(curves) > 0 {
+ var v uint16
+ if !curves.readU16(&v) {
+ return false
+ }
+ m.supportedCurves = append(m.supportedCurves, CurveID(v))
}
case extensionSupportedPoints:
// http://tools.ietf.org/html/rfc4492#section-5.5.2
- if length < 1 {
+ if !body.readU8LengthPrefixedBytes(&m.supportedPoints) || len(body) != 0 {
return false
}
- l := int(data[0])
- if length != l+1 {
- return false
- }
- m.supportedPoints = data[1 : 1+l]
case extensionSessionTicket:
// http://tools.ietf.org/html/rfc5077#section-3.2
m.ticketSupported = true
- m.sessionTicket = data[:length]
+ m.sessionTicket = []byte(body)
case extensionKeyShare:
// draft-ietf-tls-tls13 section 6.3.2.3
- if length < 2 {
+ var keyShares byteReader
+ if !body.readU16LengthPrefixed(&keyShares) || len(body) != 0 {
return false
}
- l := int(data[0])<<8 | int(data[1])
- if l != length-2 {
- return false
- }
- d := data[2:length]
m.hasKeyShares = true
- for len(d) > 0 {
- // The next KeyShareEntry contains a NamedGroup (2 bytes) and a
- // key_exchange (2-byte length prefix with at least 1 byte of content).
- if len(d) < 5 {
+ for len(keyShares) > 0 {
+ var entry keyShareEntry
+ var group uint16
+ if !keyShares.readU16(&group) ||
+ !keyShares.readU16LengthPrefixedBytes(&entry.keyExchange) {
return false
}
- entry := keyShareEntry{}
- entry.group = CurveID(d[0])<<8 | CurveID(d[1])
- keyExchLen := int(d[2])<<8 | int(d[3])
- d = d[4:]
- if len(d) < keyExchLen {
- return false
- }
- entry.keyExchange = d[:keyExchLen]
- d = d[keyExchLen:]
+ entry.group = CurveID(group)
m.keyShares = append(m.keyShares, entry)
}
case extensionPreSharedKey:
// draft-ietf-tls-tls13-18 section 4.2.6
- if length < 2 {
+ var psks, binders byteReader
+ if !body.readU16LengthPrefixed(&psks) ||
+ !body.readU16LengthPrefixed(&binders) ||
+ len(body) != 0 {
return false
}
- l := int(data[0])<<8 | int(data[1])
- d := data[2 : l+2]
- // Parse PSK identities.
- for len(d) > 0 {
- if len(d) < 2 {
+ for len(psks) > 0 {
+ var psk pskIdentity
+ if !psks.readU16LengthPrefixedBytes(&psk.ticket) ||
+ !psks.readU32(&psk.obfuscatedTicketAge) {
return false
}
- pskLen := int(d[0])<<8 | int(d[1])
- d = d[2:]
-
- if len(d) < pskLen+4 {
- return false
- }
- ticket := d[:pskLen]
- obfuscatedTicketAge := uint32(d[pskLen])<<24 | uint32(d[pskLen+1])<<16 | uint32(d[pskLen+2])<<8 | uint32(d[pskLen+3])
- psk := pskIdentity{
- ticket: ticket,
- obfuscatedTicketAge: obfuscatedTicketAge,
- }
m.pskIdentities = append(m.pskIdentities, psk)
- d = d[pskLen+4:]
}
- d = data[l+2:]
- if len(d) < 2 {
- return false
- }
- l = int(d[0])<<8 | int(d[1])
- d = d[2:]
- if l != len(d) {
- return false
- }
- // Parse PSK binders.
- for len(d) > 0 {
- if len(d) < 1 {
+ for len(binders) > 0 {
+ var binder []byte
+ if !binders.readU8LengthPrefixedBytes(&binder) {
return false
}
- binderLen := int(d[0])
- d = d[1:]
- if binderLen > len(d) {
- return false
- }
- m.pskBinders = append(m.pskBinders, d[:binderLen])
- d = d[binderLen:]
+ m.pskBinders = append(m.pskBinders, binder)
}
// There must be the same number of identities as binders.
@@ -718,121 +766,88 @@
}
case extensionPSKKeyExchangeModes:
// draft-ietf-tls-tls13-18 section 4.2.7
- if length < 1 {
+ if !body.readU8LengthPrefixedBytes(&m.pskKEModes) || len(body) != 0 {
return false
}
- l := int(data[0])
- if l != length-1 {
- return false
- }
- m.pskKEModes = data[1:length]
case extensionEarlyData:
// draft-ietf-tls-tls13 section 6.3.2.5
- if length != 0 {
+ if len(body) != 0 {
return false
}
m.hasEarlyData = true
case extensionCookie:
- if length < 2 {
+ if !body.readU16LengthPrefixedBytes(&m.tls13Cookie) || len(body) != 0 {
return false
}
- l := int(data[0])<<8 | int(data[1])
- if l != length-2 || l == 0 {
- return false
- }
- m.tls13Cookie = data[2 : 2+l]
case extensionSignatureAlgorithms:
// https://tools.ietf.org/html/rfc5246#section-7.4.1.4.1
- if length < 2 || length&1 != 0 {
+ if !parseSignatureAlgorithms(&body, &m.signatureAlgorithms) || len(body) != 0 {
return false
}
- l := int(data[0])<<8 | int(data[1])
- if l != length-2 {
- return false
- }
- n := l / 2
- d := data[2:]
- m.signatureAlgorithms = make([]signatureAlgorithm, n)
- for i := range m.signatureAlgorithms {
- m.signatureAlgorithms[i] = signatureAlgorithm(d[0])<<8 | signatureAlgorithm(d[1])
- d = d[2:]
- }
case extensionSupportedVersions:
- if length < 1+2 {
+ var versions byteReader
+ if !body.readU8LengthPrefixed(&versions) || len(body) != 0 {
return false
}
- l := int(data[0])
- if l != length-1 || l%2 == 1 || l < 2 {
- return false
- }
- n := l / 2
- d := data[1:]
- m.supportedVersions = make([]uint16, n)
- for i := range m.supportedVersions {
- m.supportedVersions[i] = uint16(d[0])<<8 | uint16(d[1])
- d = d[2:]
- }
- case extensionRenegotiationInfo:
- if length < 1 || length != int(data[0])+1 {
- return false
- }
- m.secureRenegotiation = data[1:length]
- case extensionALPN:
- if length < 2 {
- return false
- }
- l := int(data[0])<<8 | int(data[1])
- if l != length-2 {
- return false
- }
- d := data[2:length]
- for len(d) != 0 {
- stringLen := int(d[0])
- d = d[1:]
- if stringLen == 0 || stringLen > len(d) {
+ m.supportedVersions = make([]uint16, 0, len(versions)/2)
+ for len(versions) > 0 {
+ var v uint16
+ if !versions.readU16(&v) {
return false
}
- m.alpnProtocols = append(m.alpnProtocols, string(d[:stringLen]))
- d = d[stringLen:]
+ m.supportedVersions = append(m.supportedVersions, v)
+ }
+ case extensionRenegotiationInfo:
+ if !body.readU8LengthPrefixedBytes(&m.secureRenegotiation) || len(body) != 0 {
+ return false
+ }
+ case extensionALPN:
+ var protocols byteReader
+ if !body.readU16LengthPrefixed(&protocols) || len(body) != 0 {
+ return false
+ }
+ for len(protocols) > 0 {
+ var protocol []byte
+ if !protocols.readU8LengthPrefixedBytes(&protocol) {
+ return false
+ }
+ m.alpnProtocols = append(m.alpnProtocols, string(protocol))
}
case extensionChannelID:
- if length > 0 {
+ if len(body) != 0 {
return false
}
m.channelIDSupported = true
case extensionExtendedMasterSecret:
- if length != 0 {
+ if len(body) != 0 {
return false
}
m.extendedMasterSecret = true
case extensionUseSRTP:
- if length < 2 {
+ var profiles byteReader
+ var mki []byte
+ if !body.readU16LengthPrefixed(&profiles) ||
+ !body.readU8LengthPrefixedBytes(&mki) ||
+ len(body) != 0 {
return false
}
- l := int(data[0])<<8 | int(data[1])
- if l > length-2 || l%2 != 0 {
- return false
+ m.srtpProtectionProfiles = make([]uint16, 0, len(profiles)/2)
+ for len(profiles) > 0 {
+ var v uint16
+ if !profiles.readU16(&v) {
+ return false
+ }
+ m.srtpProtectionProfiles = append(m.srtpProtectionProfiles, v)
}
- n := l / 2
- m.srtpProtectionProfiles = make([]uint16, n)
- d := data[2:length]
- for i := 0; i < n; i++ {
- m.srtpProtectionProfiles[i] = uint16(d[0])<<8 | uint16(d[1])
- d = d[2:]
- }
- if len(d) < 1 || int(d[0]) != len(d)-1 {
- return false
- }
- m.srtpMasterKeyIdentifier = string(d[1:])
+ m.srtpMasterKeyIdentifier = string(mki)
case extensionSignedCertificateTimestamp:
- if length != 0 {
+ if len(body) != 0 {
return false
}
m.sctListSupported = true
case extensionCustom:
- m.customExtension = string(data[:length])
+ m.customExtension = string(body)
}
- data = data[length:]
if isGREASEValue(extension) {
m.hasGREASEExtension = true
@@ -955,76 +970,56 @@
}
func (m *serverHelloMsg) unmarshal(data []byte) bool {
- if len(data) < 42 {
+ m.raw = data
+ reader := byteReader(data[4:])
+ if !reader.readU16(&m.vers) ||
+ !reader.readBytes(&m.random, 32) {
return false
}
- m.raw = data
- m.vers = uint16(data[4])<<8 | uint16(data[5])
vers, ok := wireToVersion(m.vers, m.isDTLS)
if !ok {
return false
}
- m.random = data[6:38]
- data = data[38:]
if vers < VersionTLS13 || isResumptionExperiment(m.vers) {
- sessionIdLen := int(data[0])
- if sessionIdLen > 32 || len(data) < 1+sessionIdLen {
+ if !reader.readU8LengthPrefixedBytes(&m.sessionId) {
return false
}
- m.sessionId = data[1 : 1+sessionIdLen]
- data = data[1+sessionIdLen:]
}
- if len(data) < 2 {
+ if !reader.readU16(&m.cipherSuite) {
return false
}
- m.cipherSuite = uint16(data[0])<<8 | uint16(data[1])
- data = data[2:]
if vers < VersionTLS13 || isResumptionExperiment(m.vers) {
- if len(data) < 1 {
+ if !reader.readU8(&m.compressionMethod) {
return false
}
- m.compressionMethod = data[0]
- data = data[1:]
}
- if len(data) == 0 && m.vers < VersionTLS13 {
+ if len(reader) == 0 && m.vers < VersionTLS13 {
// Extension data is optional before TLS 1.3.
m.extensions = serverExtensions{}
m.omitExtensions = true
return true
}
- if len(data) < 2 {
- return false
- }
- extensionsLength := int(data[0])<<8 | int(data[1])
- data = data[2:]
- if len(data) != extensionsLength {
+ var extensions byteReader
+ if !reader.readU16LengthPrefixed(&extensions) || len(reader) != 0 {
return false
}
// Parse out the version from supported_versions if available.
if m.vers == VersionTLS12 {
- vdata := data
- for len(vdata) != 0 {
- if len(vdata) < 4 {
+ extensionsCopy := extensions
+ for len(extensionsCopy) > 0 {
+ var extension uint16
+ var body byteReader
+ if !extensionsCopy.readU16(&extension) ||
+ !extensionsCopy.readU16LengthPrefixed(&body) {
return false
}
- extension := uint16(vdata[0])<<8 | uint16(vdata[1])
- length := int(vdata[2])<<8 | int(vdata[3])
- vdata = vdata[4:]
-
- if len(vdata) < length {
- return false
- }
- d := vdata[:length]
- vdata = vdata[length:]
-
if extension == extensionSupportedVersions {
- if len(d) < 2 {
+ if !body.readU16(&m.vers) || len(body) != 0 {
return false
}
- m.vers = uint16(d[0])<<8 | uint16(d[1])
vers, ok = wireToVersion(m.vers, m.isDTLS)
if !ok {
return false
@@ -1034,38 +1029,27 @@
}
if vers >= VersionTLS13 {
- for len(data) != 0 {
- if len(data) < 4 {
+ for len(extensions) > 0 {
+ var extension uint16
+ var body byteReader
+ if !extensions.readU16(&extension) ||
+ !extensions.readU16LengthPrefixed(&body) {
return false
}
- extension := uint16(data[0])<<8 | uint16(data[1])
- length := int(data[2])<<8 | int(data[3])
- data = data[4:]
-
- if len(data) < length {
- return false
- }
- d := data[:length]
- data = data[length:]
-
switch extension {
case extensionKeyShare:
m.hasKeyShare = true
- if len(d) < 4 {
+ var group uint16
+ if !body.readU16(&group) ||
+ !body.readU16LengthPrefixedBytes(&m.keyShare.keyExchange) ||
+ len(body) != 0 {
return false
}
- m.keyShare.group = CurveID(uint16(d[0])<<8 | uint16(d[1]))
- keyExchLen := int(d[2])<<8 | int(d[3])
- if keyExchLen != len(d)-4 {
- return false
- }
- m.keyShare.keyExchange = make([]byte, keyExchLen)
- copy(m.keyShare.keyExchange, d[4:])
+ m.keyShare.group = CurveID(group)
case extensionPreSharedKey:
- if len(d) != 2 {
+ if !body.readU16(&m.pskIdentity) || len(body) != 0 {
return false
}
- m.pskIdentity = uint16(d[0])<<8 | uint16(d[1])
m.hasPSKIdentity = true
case extensionSupportedVersions:
if !isResumptionExperiment(m.vers) {
@@ -1077,7 +1061,7 @@
return false
}
}
- } else if !m.extensions.unmarshal(data, vers) {
+ } else if !m.extensions.unmarshal(extensions, vers) {
return false
}
@@ -1109,23 +1093,12 @@
func (m *encryptedExtensionsMsg) unmarshal(data []byte) bool {
m.raw = data
- if len(data) < 6 {
+ reader := byteReader(data[4:])
+ var extensions byteReader
+ if !reader.readU16LengthPrefixed(&extensions) || len(reader) != 0 {
return false
}
- if data[0] != typeEncryptedExtensions {
- return false
- }
- msgLen := int(data[1])<<16 | int(data[2])<<8 | int(data[3])
- data = data[4:]
- if len(data) != msgLen {
- return false
- }
- extLen := int(data[0])<<8 | int(data[1])
- data = data[2:]
- if extLen != len(data) {
- return false
- }
- return m.extensions.unmarshal(data, VersionTLS13)
+ return m.extensions.unmarshal(extensions, VersionTLS13)
}
type serverExtensions struct {
@@ -1211,8 +1184,7 @@
extension := extensions.addU16LengthPrefixed()
srtpProtectionProfiles := extension.addU16LengthPrefixed()
- srtpProtectionProfiles.addU8(byte(m.srtpProtectionProfile >> 8))
- srtpProtectionProfiles.addU8(byte(m.srtpProtectionProfile))
+ srtpProtectionProfiles.addU16(m.srtpProtectionProfile)
srtpMki := extension.addU8LengthPrefixed()
srtpMki.addBytes([]byte(m.srtpMasterKeyIdentifier))
}
@@ -1276,96 +1248,77 @@
}
}
-func (m *serverExtensions) unmarshal(data []byte, version uint16) bool {
+func (m *serverExtensions) unmarshal(data byteReader, version uint16) bool {
// Reset all fields.
*m = serverExtensions{}
- for len(data) != 0 {
- if len(data) < 4 {
+ for len(data) > 0 {
+ var extension uint16
+ var body byteReader
+ if !data.readU16(&extension) ||
+ !data.readU16LengthPrefixed(&body) {
return false
}
- extension := uint16(data[0])<<8 | uint16(data[1])
- length := int(data[2])<<8 | int(data[3])
- data = data[4:]
- if len(data) < length {
- return false
- }
-
switch extension {
case extensionNextProtoNeg:
m.nextProtoNeg = true
- d := data[:length]
- for len(d) > 0 {
- l := int(d[0])
- d = d[1:]
- if l == 0 || l > len(d) {
+ for len(body) > 0 {
+ var protocol []byte
+ if !body.readU8LengthPrefixedBytes(&protocol) {
return false
}
- m.nextProtos = append(m.nextProtos, string(d[:l]))
- d = d[l:]
+ m.nextProtos = append(m.nextProtos, string(protocol))
}
case extensionStatusRequest:
- if length > 0 {
+ if len(body) != 0 {
return false
}
m.ocspStapling = true
case extensionSessionTicket:
- if length > 0 {
+ if len(body) != 0 {
return false
}
m.ticketSupported = true
case extensionRenegotiationInfo:
- if length < 1 || length != int(data[0])+1 {
+ if !body.readU8LengthPrefixedBytes(&m.secureRenegotiation) || len(body) != 0 {
return false
}
- m.secureRenegotiation = data[1:length]
case extensionALPN:
- d := data[:length]
- if len(d) < 3 {
+ var protocols, protocol byteReader
+ if !body.readU16LengthPrefixed(&protocols) ||
+ len(body) != 0 ||
+ !protocols.readU8LengthPrefixed(&protocol) ||
+ len(protocols) != 0 {
return false
}
- l := int(d[0])<<8 | int(d[1])
- if l != len(d)-2 {
- return false
- }
- d = d[2:]
- l = int(d[0])
- if l != len(d)-1 {
- return false
- }
- d = d[1:]
- m.alpnProtocol = string(d)
- m.alpnProtocolEmpty = len(d) == 0
+ m.alpnProtocol = string(protocol)
+ m.alpnProtocolEmpty = len(protocol) == 0
case extensionChannelID:
- if length > 0 {
+ if len(body) != 0 {
return false
}
m.channelIDRequested = true
case extensionExtendedMasterSecret:
- if length != 0 {
+ if len(body) != 0 {
return false
}
m.extendedMasterSecret = true
case extensionUseSRTP:
- if length < 2+2+1 {
+ var profiles, mki byteReader
+ if !body.readU16LengthPrefixed(&profiles) ||
+ !profiles.readU16(&m.srtpProtectionProfile) ||
+ len(profiles) != 0 ||
+ !body.readU8LengthPrefixed(&mki) ||
+ len(body) != 0 {
return false
}
- if data[0] != 0 || data[1] != 2 {
- return false
- }
- m.srtpProtectionProfile = uint16(data[2])<<8 | uint16(data[3])
- d := data[4:length]
- l := int(d[0])
- if l != len(d)-1 {
- return false
- }
- m.srtpMasterKeyIdentifier = string(d[1:])
+ m.srtpMasterKeyIdentifier = string(mki)
case extensionSignedCertificateTimestamp:
- m.sctList = data[:length]
+ m.sctList = []byte(body)
case extensionCustom:
- m.customExtension = string(data[:length])
+ m.customExtension = string(body)
case extensionServerName:
- if length != 0 {
+ if len(body) != 0 {
return false
}
m.serverNameAck = true
@@ -1375,21 +1328,16 @@
return false
}
// http://tools.ietf.org/html/rfc4492#section-5.5.2
- if length < 1 {
+ if !body.readU8LengthPrefixedBytes(&m.supportedPoints) || len(body) != 0 {
return false
}
- l := int(data[0])
- if length != l+1 {
- return false
- }
- m.supportedPoints = data[1 : 1+l]
case extensionSupportedCurves:
// The server can only send supported_curves in TLS 1.3.
if version < VersionTLS13 {
return false
}
case extensionEarlyData:
- if version < VersionTLS13 || length != 0 {
+ if version < VersionTLS13 || len(body) != 0 {
return false
}
m.hasEarlyData = true
@@ -1397,7 +1345,6 @@
// Unknown extensions are illegal from the server.
return false
}
- data = data[length:]
}
return true
@@ -1409,6 +1356,7 @@
isServerHello bool
sessionId []byte
cipherSuite uint16
+ compressionMethod uint8
hasSelectedGroup bool
selectedGroup CurveID
cookie []byte
@@ -1435,7 +1383,7 @@
sessionId := retryRequest.addU8LengthPrefixed()
sessionId.addBytes(m.sessionId)
retryRequest.addU16(m.cipherSuite)
- retryRequest.addU8(0)
+ retryRequest.addU8(m.compressionMethod)
} else {
retryRequest.addU16(m.vers)
if isDraft21(m.vers) {
@@ -1478,73 +1426,56 @@
func (m *helloRetryRequestMsg) unmarshal(data []byte) bool {
m.raw = data
- if len(data) < 8 {
+ reader := byteReader(data[4:])
+ if !reader.readU16(&m.vers) {
return false
}
- m.vers = uint16(data[4])<<8 | uint16(data[5])
- data = data[6:]
if m.isServerHello {
- if len(data) < 33 {
+ var random []byte
+ var compressionMethod byte
+ if !reader.readBytes(&random, 32) ||
+ !reader.readU8LengthPrefixedBytes(&m.sessionId) ||
+ !reader.readU16(&m.cipherSuite) ||
+ !reader.readU8(&compressionMethod) ||
+ compressionMethod != 0 {
return false
}
- data = data[32:] // Random
- sessionIdLen := int(data[0])
- if sessionIdLen > 32 || len(data) < 1+sessionIdLen+3 {
- return false
- }
- m.sessionId = data[1 : 1+sessionIdLen]
- data = data[1+sessionIdLen:]
- m.cipherSuite = uint16(data[0])<<8 | uint16(data[1])
- data = data[2:]
- data = data[1:] // Compression Method
- } else {
- if isDraft21(m.vers) {
- m.cipherSuite = uint16(data[0])<<8 | uint16(data[1])
- data = data[2:]
- }
- }
- extLen := int(data[0])<<8 | int(data[1])
- data = data[2:]
- if len(data) != extLen || len(data) == 0 {
+ } else if isDraft21(m.vers) && !reader.readU16(&m.cipherSuite) {
return false
}
- for len(data) > 0 {
- if len(data) < 4 {
+ var extensions byteReader
+ if !reader.readU16LengthPrefixed(&extensions) || len(reader) != 0 {
+ return false
+ }
+ for len(extensions) > 0 {
+ var extension uint16
+ var body byteReader
+ if !extensions.readU16(&extension) ||
+ !extensions.readU16LengthPrefixed(&body) {
return false
}
- extension := uint16(data[0])<<8 | uint16(data[1])
- length := int(data[2])<<8 | int(data[3])
- data = data[4:]
- if len(data) < length {
- return false
- }
-
switch extension {
case extensionSupportedVersions:
- if length != 2 || !m.isServerHello {
+ if !m.isServerHello ||
+ !body.readU16(&m.vers) ||
+ len(body) != 0 {
return false
}
- m.vers = uint16(data[0])<<8 | uint16(data[1])
case extensionKeyShare:
- if length != 2 {
+ var v uint16
+ if !body.readU16(&v) || len(body) != 0 {
return false
}
m.hasSelectedGroup = true
- m.selectedGroup = CurveID(data[0])<<8 | CurveID(data[1])
+ m.selectedGroup = CurveID(v)
case extensionCookie:
- if length < 2 {
+ if !body.readU16LengthPrefixedBytes(&m.cookie) || len(body) != 0 {
return false
}
- cookieLen := int(data[0])<<8 | int(data[1])
- if 2+cookieLen != length {
- return false
- }
- m.cookie = data[2 : 2+cookieLen]
default:
// Unknown extensions are illegal from the server.
return false
}
- data = data[length:]
}
return true
}
@@ -1613,85 +1544,46 @@
}
func (m *certificateMsg) unmarshal(data []byte) bool {
- if len(data) < 4 {
- return false
- }
-
m.raw = data
- data = data[4:]
+ reader := byteReader(data[4:])
- if m.hasRequestContext {
- if len(data) == 0 {
- return false
- }
- contextLen := int(data[0])
- if len(data) < 1+contextLen {
- return false
- }
- m.requestContext = make([]byte, contextLen)
- copy(m.requestContext, data[1:])
- data = data[1+contextLen:]
- }
-
- if len(data) < 3 {
- return false
- }
- certsLen := int(data[0])<<16 | int(data[1])<<8 | int(data[2])
- data = data[3:]
- if len(data) != certsLen {
+ if m.hasRequestContext && !reader.readU8LengthPrefixedBytes(&m.requestContext) {
return false
}
+ var certs byteReader
+ if !reader.readU24LengthPrefixed(&certs) || len(reader) != 0 {
+ return false
+ }
m.certificates = nil
- for len(data) != 0 {
- if len(data) < 3 {
+ for len(certs) > 0 {
+ var cert certificateEntry
+ if !certs.readU24LengthPrefixedBytes(&cert.data) {
return false
}
- certLen := int(data[0])<<16 | int(data[1])<<8 | int(data[2])
- if len(data) < 3+certLen {
- return false
- }
- cert := certificateEntry{
- data: data[3 : 3+certLen],
- }
- data = data[3+certLen:]
if m.hasRequestContext {
- if len(data) < 2 {
+ var extensions byteReader
+ if !certs.readU16LengthPrefixed(&extensions) {
return false
}
- extensionsLen := int(data[0])<<8 | int(data[1])
- if len(data) < 2+extensionsLen {
- return false
- }
- extensions := data[2 : 2+extensionsLen]
- data = data[2+extensionsLen:]
- for len(extensions) != 0 {
- if len(extensions) < 4 {
+ for len(extensions) > 0 {
+ var extension uint16
+ var body byteReader
+ if !extensions.readU16(&extension) ||
+ !extensions.readU16LengthPrefixed(&body) {
return false
}
- extension := uint16(extensions[0])<<8 | uint16(extensions[1])
- length := int(extensions[2])<<8 | int(extensions[3])
- if len(extensions) < 4+length {
- return false
- }
- contents := extensions[4 : 4+length]
- extensions = extensions[4+length:]
-
switch extension {
case extensionStatusRequest:
- if length < 4 {
+ var statusType byte
+ if !body.readU8(&statusType) ||
+ statusType != statusTypeOCSP ||
+ !body.readU24LengthPrefixedBytes(&cert.ocspResponse) ||
+ len(body) != 0 {
return false
}
- if contents[0] != statusTypeOCSP {
- return false
- }
- respLen := int(contents[1])<<16 | int(contents[2])<<8 | int(contents[3])
- if respLen+4 != len(contents) || respLen == 0 {
- return false
- }
- cert.ocspResponse = contents[4:]
case extensionSignedCertificateTimestamp:
- cert.sctList = contents
+ cert.sctList = []byte(body)
default:
return false
}
@@ -1712,16 +1604,11 @@
if m.raw != nil {
return m.raw
}
- length := len(m.key)
- x := make([]byte, length+4)
- x[0] = typeServerKeyExchange
- x[1] = uint8(length >> 16)
- x[2] = uint8(length >> 8)
- x[3] = uint8(length)
- copy(x[4:], m.key)
-
- m.raw = x
- return x
+ msg := newByteBuilder()
+ msg.addU8(typeServerKeyExchange)
+ msg.addU24LengthPrefixed().addBytes(m.key)
+ m.raw = msg.finish()
+ return m.raw
}
func (m *serverKeyExchangeMsg) unmarshal(data []byte) bool {
@@ -1746,19 +1633,12 @@
var x []byte
if m.statusType == statusTypeOCSP {
- x = make([]byte, 4+4+len(m.response))
- x[0] = typeCertificateStatus
- l := len(m.response) + 4
- x[1] = byte(l >> 16)
- x[2] = byte(l >> 8)
- x[3] = byte(l)
- x[4] = statusTypeOCSP
-
- l -= 4
- x[5] = byte(l >> 16)
- x[6] = byte(l >> 8)
- x[7] = byte(l)
- copy(x[8:], m.response)
+ msg := newByteBuilder()
+ msg.addU8(typeCertificateStatus)
+ body := msg.addU24LengthPrefixed()
+ body.addU8(statusTypeOCSP)
+ body.addU24LengthPrefixed().addBytes(m.response)
+ x = msg.finish()
} else {
x = []byte{typeCertificateStatus, 0, 0, 1, m.statusType}
}
@@ -1769,22 +1649,13 @@
func (m *certificateStatusMsg) unmarshal(data []byte) bool {
m.raw = data
- if len(data) < 5 {
+ reader := byteReader(data[4:])
+ if !reader.readU8(&m.statusType) ||
+ m.statusType != statusTypeOCSP ||
+ !reader.readU24LengthPrefixedBytes(&m.response) ||
+ len(reader) != 0 {
return false
}
- m.statusType = data[4]
-
- m.response = nil
- if m.statusType == statusTypeOCSP {
- if len(data) < 8 {
- return false
- }
- respLen := uint32(data[5])<<16 | uint32(data[6])<<8 | uint32(data[7])
- if uint32(len(data)) != 4+4+respLen {
- return false
- }
- m.response = data[8:]
- }
return true
}
@@ -1809,16 +1680,11 @@
if m.raw != nil {
return m.raw
}
- length := len(m.ciphertext)
- x := make([]byte, length+4)
- x[0] = typeClientKeyExchange
- x[1] = uint8(length >> 16)
- x[2] = uint8(length >> 8)
- x[3] = uint8(length)
- copy(x[4:], m.ciphertext)
-
- m.raw = x
- return x
+ msg := newByteBuilder()
+ msg.addU8(typeClientKeyExchange)
+ msg.addU24LengthPrefixed().addBytes(m.ciphertext)
+ m.raw = msg.finish()
+ return m.raw
}
func (m *clientKeyExchangeMsg) unmarshal(data []byte) bool {
@@ -1839,17 +1705,16 @@
verifyData []byte
}
-func (m *finishedMsg) marshal() (x []byte) {
+func (m *finishedMsg) marshal() []byte {
if m.raw != nil {
return m.raw
}
- x = make([]byte, 4+len(m.verifyData))
- x[0] = typeFinished
- x[3] = byte(len(m.verifyData))
- copy(x[4:], m.verifyData)
- m.raw = x
- return
+ msg := newByteBuilder()
+ msg.addU8(typeFinished)
+ msg.addU24LengthPrefixed().addBytes(m.verifyData)
+ m.raw = msg.finish()
+ return m.raw
}
func (m *finishedMsg) unmarshal(data []byte) bool {
@@ -1870,52 +1735,38 @@
if m.raw != nil {
return m.raw
}
- l := len(m.proto)
- if l > 255 {
- l = 255
- }
- padding := 32 - (l+2)%32
- length := l + padding + 2
- x := make([]byte, length+4)
- x[0] = typeNextProtocol
- x[1] = uint8(length >> 16)
- x[2] = uint8(length >> 8)
- x[3] = uint8(length)
+ padding := 32 - (len(m.proto)+2)%32
- y := x[4:]
- y[0] = byte(l)
- copy(y[1:], []byte(m.proto[0:l]))
- y = y[1+l:]
- y[0] = byte(padding)
-
- m.raw = x
-
- return x
+ msg := newByteBuilder()
+ msg.addU8(typeNextProtocol)
+ body := msg.addU24LengthPrefixed()
+ body.addU8LengthPrefixed().addBytes([]byte(m.proto))
+ body.addU8LengthPrefixed().addBytes(make([]byte, padding))
+ m.raw = msg.finish()
+ return m.raw
}
func (m *nextProtoMsg) unmarshal(data []byte) bool {
m.raw = data
+ reader := byteReader(data[4:])
+ var proto, padding []byte
+ if !reader.readU8LengthPrefixedBytes(&proto) ||
+ !reader.readU8LengthPrefixedBytes(&padding) ||
+ len(reader) != 0 {
+ return false
+ }
+ m.proto = string(proto)
- if len(data) < 5 {
+ // Padding is not meant to be checked normally, but as this is a testing
+ // implementation, we check the padding is as expected.
+ if len(padding) != 32-(len(m.proto)+2)%32 {
return false
}
- data = data[4:]
- protoLen := int(data[0])
- data = data[1:]
- if len(data) < protoLen {
- return false
- }
- m.proto = string(data[0:protoLen])
- data = data[protoLen:]
-
- if len(data) < 1 {
- return false
- }
- paddingLen := int(data[0])
- data = data[1:]
- if len(data) != paddingLen {
- return false
+ for _, v := range padding {
+ if v != 0 {
+ return false
+ }
}
return true
@@ -2014,167 +1865,72 @@
return m.raw
}
-func parseSignatureAlgorithms(data []byte) ([]signatureAlgorithm, []byte, bool) {
- if len(data) < 2 {
- return nil, nil, false
+func parseCAs(reader *byteReader, out *[][]byte) bool {
+ var cas byteReader
+ if !reader.readU16LengthPrefixed(&cas) {
+ return false
}
- sigAlgsLen := int(data[0])<<8 | int(data[1])
- data = data[2:]
- if sigAlgsLen&1 != 0 {
- return nil, nil, false
- }
- if len(data) < int(sigAlgsLen) {
- return nil, nil, false
- }
- numSigAlgs := sigAlgsLen / 2
- signatureAlgorithms := make([]signatureAlgorithm, numSigAlgs)
- for i := range signatureAlgorithms {
- signatureAlgorithms[i] = signatureAlgorithm(data[0])<<8 | signatureAlgorithm(data[1])
- data = data[2:]
- }
-
- return signatureAlgorithms, data, true
-}
-
-func parseCAs(data []byte) ([][]byte, []byte, bool) {
- if len(data) < 2 {
- return nil, nil, false
- }
- casLength := uint16(data[0])<<8 | uint16(data[1])
- data = data[2:]
- if len(data) < int(casLength) {
- return nil, nil, false
- }
-
- cas := data[:casLength]
- data = data[casLength:]
-
- var certificateAuthorities [][]byte
for len(cas) > 0 {
- if len(cas) < 2 {
- return nil, nil, false
+ var ca []byte
+ if !cas.readU16LengthPrefixedBytes(&ca) {
+ return false
}
- caLen := uint16(cas[0])<<8 | uint16(cas[1])
- cas = cas[2:]
-
- if len(cas) < int(caLen) {
- return nil, nil, false
- }
-
- certificateAuthorities = append(certificateAuthorities, cas[:caLen])
- cas = cas[caLen:]
+ *out = append(*out, ca)
}
- return certificateAuthorities, data, true
+ return true
}
func (m *certificateRequestMsg) unmarshal(data []byte) bool {
m.raw = data
+ reader := byteReader(data[4:])
- if len(data) < 5 {
- return false
- }
- data = data[4:]
-
- if m.hasRequestContext {
- contextLen := int(data[0])
- if len(data) < 1+contextLen {
+ if isDraft21(m.vers) {
+ var extensions byteReader
+ if !reader.readU8LengthPrefixedBytes(&m.requestContext) ||
+ !reader.readU16LengthPrefixed(&extensions) ||
+ len(reader) != 0 {
return false
}
- m.requestContext = make([]byte, contextLen)
- copy(m.requestContext, data[1:])
- data = data[1+contextLen:]
- if isDraft21(m.vers) {
- if len(data) < 2 {
+ for len(extensions) > 0 {
+ var extension uint16
+ var body byteReader
+ if !extensions.readU16(&extension) ||
+ !extensions.readU16LengthPrefixed(&body) {
return false
}
- extensionsLen := int(data[0])<<8 | int(data[1])
- if len(data) < 2+extensionsLen {
- return false
- }
- extensions := data[2 : 2+extensionsLen]
- data = data[2+extensionsLen:]
- for len(extensions) != 0 {
- if len(extensions) < 4 {
+ switch extension {
+ case extensionSignatureAlgorithms:
+ if !parseSignatureAlgorithms(&body, &m.signatureAlgorithms) || len(body) != 0 {
return false
}
- extension := uint16(extensions[0])<<8 | uint16(extensions[1])
- length := int(extensions[2])<<8 | int(extensions[3])
- if len(extensions) < 4+length {
+ case extensionCertificateAuthorities:
+ if !parseCAs(&body, &m.certificateAuthorities) || len(body) != 0 {
return false
}
- contents := extensions[4 : 4+length]
- extensions = extensions[4+length:]
- switch extension {
- case extensionSignatureAlgorithms:
- sigAlgs, rest, ok := parseSignatureAlgorithms(contents)
- if !ok || len(rest) != 0 {
- return false
- }
- m.signatureAlgorithms = sigAlgs
- case extensionCertificateAuthorities:
- cas, rest, ok := parseCAs(contents)
- if !ok || len(rest) != 0 {
- return false
- }
- m.hasCAExtension = true
- m.certificateAuthorities = cas
- }
+ m.hasCAExtension = true
}
- } else {
- if m.hasSignatureAlgorithm {
- sigAlgs, rest, ok := parseSignatureAlgorithms(data)
- if !ok {
- return false
- }
- m.signatureAlgorithms = sigAlgs
- data = rest
- }
-
- cas, rest, ok := parseCAs(data)
- if !ok {
- return false
- }
- m.certificateAuthorities = cas
- data = rest
-
- // Ignore certificate extensions.
- if len(data) < 2 {
- return false
- }
- extsLength := int(data[0])<<8 | int(data[1])
- if len(data) < 2+extsLength {
- return false
- }
- data = data[2+extsLength:]
}
+ } else if m.hasRequestContext {
+ var extensions byteReader
+ if !reader.readU8LengthPrefixedBytes(&m.requestContext) ||
+ !parseSignatureAlgorithms(&reader, &m.signatureAlgorithms) ||
+ !parseCAs(&reader, &m.certificateAuthorities) ||
+ !reader.readU16LengthPrefixed(&extensions) ||
+ len(reader) != 0 {
+ return false
+ }
+ // Ignore certificate extensions.
} else {
- numCertTypes := int(data[0])
- if len(data) < 1+numCertTypes {
+ if !reader.readU8LengthPrefixedBytes(&m.certificateTypes) {
return false
}
- m.certificateTypes = make([]byte, numCertTypes)
- copy(m.certificateTypes, data[1:])
- data = data[1+numCertTypes:]
-
- if m.hasSignatureAlgorithm {
- sigAlgs, rest, ok := parseSignatureAlgorithms(data)
- if !ok {
- return false
- }
- m.signatureAlgorithms = sigAlgs
- data = rest
- }
-
- cas, rest, ok := parseCAs(data)
- if !ok {
+ if m.hasSignatureAlgorithm && !parseSignatureAlgorithms(&reader, &m.signatureAlgorithms) {
return false
}
- m.certificateAuthorities = cas
- data = rest
- }
-
- if len(data) > 0 {
- return false
+ if !parseCAs(&reader, &m.certificateAuthorities) ||
+ len(reader) != 0 {
+ return false
+ }
}
return true
diff --git a/src/ssl/test/runner/handshake_server.go b/src/ssl/test/runner/handshake_server.go
index 4286911..595f8da 100644
--- a/src/ssl/test/runner/handshake_server.go
+++ b/src/ssl/test/runner/handshake_server.go
@@ -368,6 +368,7 @@
isDTLS: c.isDTLS,
vers: c.wireVersion,
sessionId: hs.clientHello.sessionId,
+ compressionMethod: config.Bugs.SendCompressionMethod,
versOverride: config.Bugs.SendServerHelloVersion,
supportedVersOverride: config.Bugs.SendServerSupportedExtensionVersion,
customExtension: config.Bugs.CustomUnencryptedExtension,
@@ -534,6 +535,7 @@
vers: c.wireVersion,
sessionId: hs.clientHello.sessionId,
cipherSuite: cipherSuite,
+ compressionMethod: config.Bugs.SendCompressionMethod,
duplicateExtensions: config.Bugs.DuplicateHelloRetryRequestExtensions,
}
diff --git a/src/ssl/test/runner/runner.go b/src/ssl/test/runner/runner.go
index 8700af2..66c13fc 100644
--- a/src/ssl/test/runner/runner.go
+++ b/src/ssl/test/runner/runner.go
@@ -2785,6 +2785,34 @@
expectedLocalError: "remote error: illegal parameter",
},
{
+ testType: clientTest,
+ name: "TLS13Draft22-InvalidCompressionMethod",
+ config: Config{
+ MaxVersion: VersionTLS13,
+ Bugs: ProtocolBugs{
+ SendCompressionMethod: 1,
+ },
+ },
+ tls13Variant: TLS13Draft22,
+ shouldFail: true,
+ expectedError: ":DECODE_ERROR:",
+ },
+ {
+ testType: clientTest,
+ name: "TLS13Draft22-HRR-InvalidCompressionMethod",
+ config: Config{
+ MaxVersion: VersionTLS13,
+ CurvePreferences: []CurveID{CurveP384},
+ Bugs: ProtocolBugs{
+ SendCompressionMethod: 1,
+ },
+ },
+ tls13Variant: TLS13Draft22,
+ shouldFail: true,
+ expectedError: ":DECODE_ERROR:",
+ expectedLocalError: "remote error: error decoding message",
+ },
+ {
name: "GREASE-Client-TLS12",
config: Config{
MaxVersion: VersionTLS12,
@@ -7268,6 +7296,25 @@
},
})
testCases = append(testCases, testCase{
+ name: "Renegotiate-Client-TLS13Draft22",
+ config: Config{
+ MaxVersion: VersionTLS12,
+ Bugs: ProtocolBugs{
+ FailIfResumeOnRenego: true,
+ },
+ },
+ tls13Variant: TLS13Draft22,
+ renegotiate: 1,
+ // Test renegotiation after both an initial and resumption
+ // handshake.
+ resumeSession: true,
+ flags: []string{
+ "-renegotiate-freely",
+ "-expect-total-renegotiations", "1",
+ "-expect-secure-renegotiation",
+ },
+ })
+ testCases = append(testCases, testCase{
name: "Renegotiate-Client-EmptyExt",
renegotiate: 1,
config: Config{
@@ -11017,6 +11064,23 @@
resumeSession: true,
})
+ // Test that the client correctly handles a TLS 1.3 ServerHello which echoes
+ // a TLS 1.2 session ID.
+ testCases = append(testCases, testCase{
+ testType: clientTest,
+ name: "TLS12SessionID-" + name,
+ config: Config{
+ MaxVersion: VersionTLS12,
+ SessionTicketsDisabled: true,
+ },
+ resumeConfig: &Config{
+ MaxVersion: VersionTLS13,
+ },
+ tls13Variant: variant,
+ resumeSession: true,
+ expectResumeRejected: true,
+ })
+
// Test that the server correctly echoes back session IDs of
// various lengths.
testCases = append(testCases, testCase{
diff --git a/src/ssl/tls13_client.cc b/src/ssl/tls13_client.cc
index 688fa06..f471a4e 100644
--- a/src/ssl/tls13_client.cc
+++ b/src/ssl/tls13_client.cc
@@ -75,11 +75,14 @@
CBS body = msg.body, server_random, session_id;
uint16_t server_version;
+ uint8_t compression_method;
if (!CBS_get_u16(&body, &server_version) ||
!CBS_get_bytes(&body, &server_random, SSL3_RANDOM_SIZE) ||
!CBS_get_u8_length_prefixed(&body, &session_id) ||
+ !CBS_mem_equal(&session_id, hs->session_id, hs->session_id_len) ||
!CBS_get_u16(&body, &cipher_suite) ||
- !CBS_skip(&body, 1) ||
+ !CBS_get_u8(&body, &compression_method) ||
+ compression_method != 0 ||
!CBS_get_u16_length_prefixed(&body, &extensions) ||
CBS_len(&extensions) == 0 ||
CBS_len(&body) != 0) {
@@ -251,7 +254,8 @@
if (!CBS_get_u16(&body, &server_version) ||
!CBS_get_bytes(&body, &server_random, SSL3_RANDOM_SIZE) ||
(ssl_is_resumption_experiment(ssl->version) &&
- !CBS_get_u8_length_prefixed(&body, &session_id)) ||
+ (!CBS_get_u8_length_prefixed(&body, &session_id) ||
+ !CBS_mem_equal(&session_id, hs->session_id, hs->session_id_len))) ||
!CBS_get_u16(&body, &cipher_suite) ||
(ssl_is_resumption_experiment(ssl->version) &&
(!CBS_get_u8(&body, &compression_method) || compression_method != 0)) ||
diff --git a/src/tool/client.cc b/src/tool/client.cc
index 57e1b6e..fa279ae 100644
--- a/src/tool/client.cc
+++ b/src/tool/client.cc
@@ -122,7 +122,8 @@
},
{
"-early-data", kOptionalArgument, "Enable early data. The argument to "
- "this flag is the early data to send.",
+ "this flag is the early data to send or if it starts with '@', the "
+ "file to read from for early data.",
},
{
"-tls13-variant", kOptionalArgument,
@@ -299,8 +300,19 @@
}
if (args_map.count("-early-data") != 0 && SSL_in_early_data(ssl.get())) {
- int ed_size = args_map["-early-data"].size();
- int ssl_ret = SSL_write(ssl.get(), args_map["-early-data"].data(), ed_size);
+ std::string early_data = args_map["-early-data"];
+ if (early_data.size() > 0 && early_data[0] == '@') {
+ const char *filename = early_data.c_str() + 1;
+ std::vector<uint8_t> data;
+ ScopedFILE f(fopen(filename, "rb"));
+ if (f == nullptr || !ReadAll(&data, f.get())) {
+ fprintf(stderr, "Error reading %s.\n", filename);
+ return false;
+ }
+ early_data = std::string(data.begin(), data.end());
+ }
+ int ed_size = early_data.size();
+ int ssl_ret = SSL_write(ssl.get(), early_data.data(), ed_size);
if (ssl_ret <= 0) {
int ssl_err = SSL_get_error(ssl.get(), ssl_ret);
fprintf(stderr, "Error while writing: %d\n", ssl_err);
diff --git a/src/tool/generate_ed25519.cc b/src/tool/generate_ed25519.cc
index 35b57b9..6499dbe 100644
--- a/src/tool/generate_ed25519.cc
+++ b/src/tool/generate_ed25519.cc
@@ -21,14 +21,6 @@
#include "internal.h"
-struct FileCloser {
- void operator()(FILE *file) {
- fclose(file);
- }
-};
-
-using ScopedFILE = std::unique_ptr<FILE, FileCloser>;
-
static const struct argument kArguments[] = {
{
"-out-public", kRequiredArgument, "The file to write the public key to",
diff --git a/src/tool/internal.h b/src/tool/internal.h
index a6c8eca..b626270 100644
--- a/src/tool/internal.h
+++ b/src/tool/internal.h
@@ -44,6 +44,14 @@
#define BORINGSSL_WRITE write
#endif
+struct FileCloser {
+ void operator()(FILE *file) {
+ fclose(file);
+ }
+};
+
+using ScopedFILE = std::unique_ptr<FILE, FileCloser>;
+
enum ArgumentType {
kRequiredArgument,
kOptionalArgument,
diff --git a/src/tool/server.cc b/src/tool/server.cc
index 0061cb3..9963885 100644
--- a/src/tool/server.cc
+++ b/src/tool/server.cc
@@ -71,6 +71,9 @@
"-tls13-variant", kBooleanArgument, "Enable TLS 1.3 variants",
},
{
+ "-tls13-draft22-variant", kBooleanArgument, "Enable TLS 1.3 Draft 22.",
+ },
+ {
"-www", kBooleanArgument,
"The server will print connection information in response to a "
"HTTP GET request.",
@@ -88,14 +91,6 @@
},
};
-struct FileCloser {
- void operator()(FILE *file) {
- fclose(file);
- }
-};
-
-using ScopedFILE = std::unique_ptr<FILE, FileCloser>;
-
static bool LoadOCSPResponse(SSL_CTX *ctx, const char *filename) {
ScopedFILE f(fopen(filename, "rb"));
std::vector<uint8_t> data;
@@ -315,8 +310,10 @@
SSL_CTX_set_early_data_enabled(ctx.get(), 1);
}
- // Enabling any TLS 1.3 variant on the server enables all of them.
- if (args_map.count("-tls13-variant") != 0) {
+ // Draft 22 variants need to be explicitly enabled.
+ if (args_map.count("-tls13-draft22-variant") != 0) {
+ SSL_CTX_set_tls13_variant(ctx.get(), tls13_draft22);
+ } else if (args_map.count("-tls13-variant") != 0) {
SSL_CTX_set_tls13_variant(ctx.get(), tls13_experiment);
}