external/boringssl: Sync to 27bc0f26c8d132df04f5b0b173aefeb8aaa13c33.
This includes the following changes:
https://boringssl.googlesource.com/boringssl/+log/ba8f1864c15ec938ce0851f416663511c89f454a..27bc0f26c8d132df04f5b0b173aefeb8aaa13c33
Test: BoringSSL CTS Presubmits
Change-Id: Id63dac9fa22a3b41609f55bfe48d2cfaa53b25c6
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 338f212..eb8717a 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -238,6 +238,24 @@
set(OPENSSL_NO_ASM "1")
endif()
+if(CFI)
+ if(NOT CLANG)
+ message(FATAL_ERROR "Cannot enable CFI unless using Clang")
+ endif()
+
+ # TODO(crbug.com/785442): Remove -fsanitize-cfi-icall-generalize-pointers.
+ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=cfi -fno-sanitize-trap=cfi -fsanitize-cfi-icall-generalize-pointers -flto")
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=cfi -fno-sanitize-trap=cfi -fsanitize-cfi-icall-generalize-pointers -flto")
+ # We use Chromium's copy of clang, which requires -fuse-ld=lld if building
+ # with -flto. That, in turn, can't handle -ggdb.
+ set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fuse-ld=lld")
+ string(REPLACE "-ggdb" "-g" CMAKE_C_FLAGS "${CMAKE_C_FLAGS}")
+ string(REPLACE "-ggdb" "-g" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
+ # -flto causes object files to contain LLVM bitcode. Mixing those with
+ # assembly output in the same static library breaks the linker.
+ set(OPENSSL_NO_ASM "1")
+endif()
+
if (GCOV)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fprofile-arcs -ftest-coverage")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fprofile-arcs -ftest-coverage")
diff --git a/src/crypto/bytestring/ber.c b/src/crypto/bytestring/ber.c
index 4dc94f6..bb5e17c 100644
--- a/src/crypto/bytestring/ber.c
+++ b/src/crypto/bytestring/ber.c
@@ -29,10 +29,7 @@
// 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) {
- if ((tag & 0xc0) != 0) {
- return 0;
- }
- switch (tag & 0x1f) {
+ switch (tag & ~CBS_ASN1_CONSTRUCTED) {
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 e8e14d9..7e3d453 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, 0x1));
- EXPECT_TRUE(CBS_peek_asn1_tag(&data, 0x30));
+ EXPECT_FALSE(CBS_peek_asn1_tag(&data, CBS_ASN1_BOOLEAN));
+ EXPECT_TRUE(CBS_peek_asn1_tag(&data, CBS_ASN1_SEQUENCE));
- ASSERT_TRUE(CBS_get_asn1(&data, &contents, 0x30));
+ ASSERT_TRUE(CBS_get_asn1(&data, &contents, CBS_ASN1_SEQUENCE));
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, 0x30));
+ EXPECT_FALSE(CBS_get_asn1(&data, &contents, CBS_ASN1_SEQUENCE));
CBS_init(&data, kData3, sizeof(kData3));
// zero byte length of length
- EXPECT_FALSE(CBS_get_asn1(&data, &contents, 0x30));
+ EXPECT_FALSE(CBS_get_asn1(&data, &contents, CBS_ASN1_SEQUENCE));
CBS_init(&data, kData4, sizeof(kData4));
// long form mistakenly used.
- EXPECT_FALSE(CBS_get_asn1(&data, &contents, 0x30));
+ EXPECT_FALSE(CBS_get_asn1(&data, &contents, CBS_ASN1_SEQUENCE));
CBS_init(&data, kData5, sizeof(kData5));
// length takes too many bytes.
- EXPECT_FALSE(CBS_get_asn1(&data, &contents, 0x30));
+ EXPECT_FALSE(CBS_get_asn1(&data, &contents, CBS_ASN1_SEQUENCE));
CBS_init(&data, kData1, sizeof(kData1));
// wrong tag.
@@ -151,56 +151,72 @@
CBS_init(&data, NULL, 0);
// peek at empty data.
- EXPECT_FALSE(CBS_peek_asn1_tag(&data, 0x30));
+ EXPECT_FALSE(CBS_peek_asn1_tag(&data, CBS_ASN1_SEQUENCE));
CBS_init(&data, NULL, 0);
// optional elements at empty data.
- ASSERT_TRUE(CBS_get_optional_asn1(&data, &contents, &present, 0xa0));
+ ASSERT_TRUE(CBS_get_optional_asn1(
+ &data, &contents, &present,
+ CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0));
EXPECT_FALSE(present);
- ASSERT_TRUE(
- CBS_get_optional_asn1_octet_string(&data, &contents, &present, 0xa0));
+ ASSERT_TRUE(CBS_get_optional_asn1_octet_string(
+ &data, &contents, &present,
+ CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0));
EXPECT_FALSE(present);
EXPECT_EQ(0u, CBS_len(&contents));
- ASSERT_TRUE(CBS_get_optional_asn1_octet_string(&data, &contents, NULL, 0xa0));
+ ASSERT_TRUE(CBS_get_optional_asn1_octet_string(
+ &data, &contents, NULL,
+ CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0));
EXPECT_EQ(0u, CBS_len(&contents));
- ASSERT_TRUE(CBS_get_optional_asn1_uint64(&data, &value, 0xa0, 42));
+ ASSERT_TRUE(CBS_get_optional_asn1_uint64(
+ &data, &value, CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0, 42));
EXPECT_EQ(42u, value);
CBS_init(&data, kData6, sizeof(kData6));
// optional element.
- ASSERT_TRUE(CBS_get_optional_asn1(&data, &contents, &present, 0xa0));
+ ASSERT_TRUE(CBS_get_optional_asn1(
+ &data, &contents, &present,
+ CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0));
EXPECT_FALSE(present);
- ASSERT_TRUE(CBS_get_optional_asn1(&data, &contents, &present, 0xa1));
+ ASSERT_TRUE(CBS_get_optional_asn1(
+ &data, &contents, &present,
+ CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 1));
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, 0xa0));
+ ASSERT_TRUE(CBS_get_optional_asn1_octet_string(
+ &data, &contents, &present,
+ CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0));
EXPECT_FALSE(present);
EXPECT_EQ(0u, CBS_len(&contents));
- ASSERT_TRUE(
- CBS_get_optional_asn1_octet_string(&data, &contents, &present, 0xa1));
+ ASSERT_TRUE(CBS_get_optional_asn1_octet_string(
+ &data, &contents, &present,
+ CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 1));
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, 0xa1));
+ EXPECT_FALSE(CBS_get_optional_asn1_octet_string(
+ &data, &contents, &present,
+ CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 1));
CBS_init(&data, kData8, sizeof(kData8));
// optional integer.
- ASSERT_TRUE(CBS_get_optional_asn1_uint64(&data, &value, 0xa0, 42));
+ ASSERT_TRUE(CBS_get_optional_asn1_uint64(
+ &data, &value, CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0, 42));
EXPECT_EQ(42u, value);
- ASSERT_TRUE(CBS_get_optional_asn1_uint64(&data, &value, 0xa1, 42));
+ ASSERT_TRUE(CBS_get_optional_asn1_uint64(
+ &data, &value, CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 1, 42));
EXPECT_EQ(1u, value);
CBS_init(&data, kData9, sizeof(kData9));
// invalid optional integer.
- EXPECT_FALSE(CBS_get_optional_asn1_uint64(&data, &value, 0xa1, 42));
+ EXPECT_FALSE(CBS_get_optional_asn1_uint64(
+ &data, &value, CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 1, 42));
unsigned tag;
CBS_init(&data, kData1, sizeof(kData1));
@@ -217,6 +233,54 @@
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};
@@ -416,15 +480,42 @@
}
TEST(CBBTest, ASN1) {
- static const uint8_t kExpected[] = {0x30, 3, 1, 2, 3};
+ 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,
+ };
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, 0x30));
+ ASSERT_TRUE(CBB_add_asn1(cbb.get(), &contents, CBS_ASN1_SEQUENCE));
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);
@@ -432,7 +523,7 @@
std::vector<uint8_t> test_data(100000, 0x42);
ASSERT_TRUE(CBB_init(cbb.get(), 0));
- ASSERT_TRUE(CBB_add_asn1(cbb.get(), &contents, 0x30));
+ ASSERT_TRUE(CBB_add_asn1(cbb.get(), &contents, CBS_ASN1_SEQUENCE));
ASSERT_TRUE(CBB_add_bytes(&contents, test_data.data(), 130));
ASSERT_TRUE(CBB_finish(cbb.get(), &buf, &buf_len));
scoper.reset(buf);
@@ -442,7 +533,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, 0x30));
+ ASSERT_TRUE(CBB_add_asn1(cbb.get(), &contents, CBS_ASN1_SEQUENCE));
ASSERT_TRUE(CBB_add_bytes(&contents, test_data.data(), 1000));
ASSERT_TRUE(CBB_finish(cbb.get(), &buf, &buf_len));
scoper.reset(buf);
@@ -452,8 +543,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, 0x30));
- ASSERT_TRUE(CBB_add_asn1(&contents, &inner_contents, 0x30));
+ 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_bytes(&inner_contents, test_data.data(), 100000));
ASSERT_TRUE(CBB_finish(cbb.get(), &buf, &buf_len));
scoper.reset(buf);
@@ -490,6 +581,12 @@
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,
@@ -534,6 +631,8 @@
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,
diff --git a/src/crypto/bytestring/cbb.c b/src/crypto/bytestring/cbb.c
index 5576fa9..b1afe7d 100644
--- a/src/crypto/bytestring/cbb.c
+++ b/src/crypto/bytestring/cbb.c
@@ -329,17 +329,36 @@
}
int CBB_add_asn1(CBB *cbb, CBB *out_contents, unsigned tag) {
- if (tag > 0xff ||
- (tag & 0x1f) == 0x1f) {
- // Long form identifier octets are not supported. Further, all current valid
- // tag serializations are 8 bits.
- cbb->base->error = 1;
+ if (!CBB_flush(cbb)) {
return 0;
}
- if (!CBB_flush(cbb) ||
- // |tag|'s representation matches the DER encoding.
- !CBB_add_u8(cbb, (uint8_t)tag)) {
+ // 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)) {
return 0;
}
diff --git a/src/crypto/bytestring/cbs.c b/src/crypto/bytestring/cbs.c
index ec495d2..d96371c 100644
--- a/src/crypto/bytestring/cbs.c
+++ b/src/crypto/bytestring/cbs.c
@@ -175,18 +175,9 @@
return cbs_get_length_prefixed(cbs, out, 3);
}
-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)) {
+static int parse_asn1_tag(CBS *cbs, unsigned *out) {
+ uint8_t tag_byte;
+ if (!CBS_get_u8(cbs, &tag_byte)) {
return 0;
}
@@ -197,22 +188,70 @@
// 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).
- if ((tag & 0x1f) == 0x1f) {
- return 0;
+ 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)) {
+ 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) + 2;
+ len = ((size_t) length_byte) + header_len;
if (out_header_len != NULL) {
- *out_header_len = 2;
+ *out_header_len = header_len;
}
} else {
// The high bit indicate that this is the long form, while the next 7 bits
@@ -224,9 +263,9 @@
if (ber_ok && (tag & CBS_ASN1_CONSTRUCTED) != 0 && num_bytes == 0) {
// indefinite length
if (out_header_len != NULL) {
- *out_header_len = 2;
+ *out_header_len = header_len;
}
- return CBS_get_bytes(cbs, out, 2);
+ return CBS_get_bytes(cbs, out, header_len);
}
// ITU-T X.690 clause 8.1.3.5.c specifies that the value 0xff shall not be
@@ -249,13 +288,13 @@
return 0;
}
len = len32;
- if (len + 2 + num_bytes < len) {
+ if (len + header_len + num_bytes < len) {
// Overflow.
return 0;
}
- len += 2 + num_bytes;
+ len += header_len + num_bytes;
if (out_header_len != NULL) {
- *out_header_len = 2 + num_bytes;
+ *out_header_len = header_len + num_bytes;
}
}
@@ -323,7 +362,10 @@
if (CBS_len(cbs) < 1) {
return 0;
}
- return CBS_data(cbs)[0] == tag_value;
+
+ CBS copy = *cbs;
+ unsigned actual_tag;
+ return parse_asn1_tag(©, &actual_tag) && tag_value == actual_tag;
}
int CBS_get_asn1_uint64(CBS *cbs, uint64_t *out) {
diff --git a/src/crypto/dsa/dsa.c b/src/crypto/dsa/dsa.c
index 61f0c6c..f3d4f85 100644
--- a/src/crypto/dsa/dsa.c
+++ b/src/crypto/dsa/dsa.c
@@ -82,6 +82,9 @@
// Rabin-Miller
#define DSS_prime_checks 50
+static int dsa_sign_setup(const DSA *dsa, BN_CTX *ctx_in, BIGNUM **out_kinv,
+ BIGNUM **out_r);
+
static CRYPTO_EX_DATA_CLASS g_ex_data_class = CRYPTO_EX_DATA_CLASS_INIT;
DSA *DSA_new(void) {
@@ -117,8 +120,6 @@
BN_clear_free(dsa->g);
BN_clear_free(dsa->pub_key);
BN_clear_free(dsa->priv_key);
- BN_clear_free(dsa->kinv);
- BN_clear_free(dsa->r);
BN_MONT_CTX_free(dsa->method_mont_p);
BN_MONT_CTX_free(dsa->method_mont_q);
CRYPTO_MUTEX_cleanup(&dsa->method_mont_lock);
@@ -544,14 +545,13 @@
OPENSSL_free(sig);
}
-DSA_SIG *DSA_do_sign(const uint8_t *digest, size_t digest_len, DSA *dsa) {
+DSA_SIG *DSA_do_sign(const uint8_t *digest, size_t digest_len, const DSA *dsa) {
BIGNUM *kinv = NULL, *r = NULL, *s = NULL;
BIGNUM m;
BIGNUM xr;
BN_CTX *ctx = NULL;
int reason = ERR_R_BN_LIB;
DSA_SIG *ret = NULL;
- int noredo = 0;
BN_init(&m);
BN_init(&xr);
@@ -571,16 +571,8 @@
}
redo:
- if (dsa->kinv == NULL || dsa->r == NULL) {
- if (!DSA_sign_setup(dsa, ctx, &kinv, &r)) {
- goto err;
- }
- } else {
- kinv = dsa->kinv;
- dsa->kinv = NULL;
- r = dsa->r;
- dsa->r = NULL;
- noredo = 1;
+ if (!dsa_sign_setup(dsa, ctx, &kinv, &r)) {
+ goto err;
}
if (digest_len > BN_num_bytes(dsa->q)) {
@@ -613,10 +605,6 @@
// Redo if r or s is zero as required by FIPS 186-3: this is
// very unlikely.
if (BN_is_zero(r) || BN_is_zero(s)) {
- if (noredo) {
- reason = DSA_R_NEED_NEW_SETUP_VALUES;
- goto err;
- }
goto redo;
}
ret = DSA_SIG_new();
@@ -758,7 +746,7 @@
}
int DSA_sign(int type, const uint8_t *digest, size_t digest_len,
- uint8_t *out_sig, unsigned int *out_siglen, DSA *dsa) {
+ uint8_t *out_sig, unsigned int *out_siglen, const DSA *dsa) {
DSA_SIG *s;
s = DSA_do_sign(digest, digest_len, dsa);
@@ -848,8 +836,8 @@
return ret;
}
-int DSA_sign_setup(const DSA *dsa, BN_CTX *ctx_in, BIGNUM **out_kinv,
- BIGNUM **out_r) {
+static int dsa_sign_setup(const DSA *dsa, BN_CTX *ctx_in, BIGNUM **out_kinv,
+ BIGNUM **out_r) {
BN_CTX *ctx;
BIGNUM k, kq, *kinv = NULL, *r = NULL;
int ret = 0;
diff --git a/src/crypto/ec_extra/ec_asn1.c b/src/crypto/ec_extra/ec_asn1.c
index dc710a8..c125af2 100644
--- a/src/crypto/ec_extra/ec_asn1.c
+++ b/src/crypto/ec_extra/ec_asn1.c
@@ -67,9 +67,9 @@
#include "../internal.h"
-static const uint8_t kParametersTag =
+static const unsigned kParametersTag =
CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 0;
-static const uint8_t kPublicKeyTag =
+static const unsigned kPublicKeyTag =
CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 1;
EC_KEY *EC_KEY_parse_private_key(CBS *cbs, const EC_GROUP *group) {
diff --git a/src/crypto/ecdsa_extra/ecdsa_asn1.c b/src/crypto/ecdsa_extra/ecdsa_asn1.c
index 8d0bc41..fbf4cca 100644
--- a/src/crypto/ecdsa_extra/ecdsa_asn1.c
+++ b/src/crypto/ecdsa_extra/ecdsa_asn1.c
@@ -73,13 +73,6 @@
(EC_KEY*) eckey /* cast away const */);
}
- return ECDSA_sign_ex(type, digest, digest_len, sig, sig_len, NULL, NULL,
- eckey);
-}
-
-int ECDSA_sign_ex(int type, const uint8_t *digest, size_t digest_len,
- uint8_t *sig, unsigned int *sig_len, const BIGNUM *kinv,
- const BIGNUM *r, const EC_KEY *eckey) {
int ret = 0;
ECDSA_SIG *s = NULL;
@@ -89,7 +82,7 @@
goto err;
}
- s = ECDSA_do_sign_ex(digest, digest_len, kinv, r, eckey);
+ s = ECDSA_do_sign(digest, digest_len, eckey);
if (s == NULL) {
*sig_len = 0;
goto err;
diff --git a/src/crypto/err/ec.errordata b/src/crypto/err/ec.errordata
index fae9480..de8ee6c 100644
--- a/src/crypto/err/ec.errordata
+++ b/src/crypto/err/ec.errordata
@@ -17,6 +17,7 @@
EC,111,INVALID_FORM
EC,112,INVALID_GROUP_ORDER
EC,113,INVALID_PRIVATE_KEY
+EC,133,INVALID_SCALAR
EC,114,MISSING_PARAMETERS
EC,115,MISSING_PRIVATE_KEY
EC,116,NON_NAMED_CURVE
diff --git a/src/crypto/err/ssl.errordata b/src/crypto/err/ssl.errordata
index a528874..dec7347 100644
--- a/src/crypto/err/ssl.errordata
+++ b/src/crypto/err/ssl.errordata
@@ -53,6 +53,7 @@
SSL,257,DUPLICATE_EXTENSION
SSL,264,DUPLICATE_KEY_SHARE
SSL,144,ECC_CERT_NOT_FOR_SIGNING
+SSL,282,EMPTY_HELLO_RETRY_REQUEST
SSL,145,EMS_STATE_INCONSISTENT
SSL,146,ENCRYPTED_LENGTH_TOO_LONG
SSL,147,ERROR_ADDING_EXTENSION
diff --git a/src/crypto/evp/p_rsa_asn1.c b/src/crypto/evp/p_rsa_asn1.c
index a188538..85f6fc8 100644
--- a/src/crypto/evp/p_rsa_asn1.c
+++ b/src/crypto/evp/p_rsa_asn1.c
@@ -161,7 +161,7 @@
}
static int rsa_bits(const EVP_PKEY *pkey) {
- return BN_num_bits(pkey->pkey.rsa->n);
+ return RSA_bits(pkey->pkey.rsa);
}
static void int_rsa_free(EVP_PKEY *pkey) { RSA_free(pkey->pkey.rsa); }
diff --git a/src/crypto/fipsmodule/bn/asm/x86_64-gcc.c b/src/crypto/fipsmodule/bn/asm/x86_64-gcc.c
index bcf12eb..49351c1 100644
--- a/src/crypto/fipsmodule/bn/asm/x86_64-gcc.c
+++ b/src/crypto/fipsmodule/bn/asm/x86_64-gcc.c
@@ -93,11 +93,11 @@
#undef sqr
#define sqr(r0, r1, a) __asm__("mulq %2" : "=a"(r0), "=d"(r1) : "a"(a) : "cc");
-BN_ULONG bn_mul_add_words(BN_ULONG *rp, const BN_ULONG *ap, int num,
+BN_ULONG bn_mul_add_words(BN_ULONG *rp, const BN_ULONG *ap, size_t num,
BN_ULONG w) {
BN_ULONG c1 = 0;
- if (num <= 0) {
+ if (num == 0) {
return (c1);
}
@@ -126,10 +126,11 @@
return c1;
}
-BN_ULONG bn_mul_words(BN_ULONG *rp, const BN_ULONG *ap, int num, BN_ULONG w) {
+BN_ULONG bn_mul_words(BN_ULONG *rp, const BN_ULONG *ap, size_t num,
+ BN_ULONG w) {
BN_ULONG c1 = 0;
- if (num <= 0) {
+ if (num == 0) {
return c1;
}
@@ -156,8 +157,8 @@
return c1;
}
-void bn_sqr_words(BN_ULONG *r, const BN_ULONG *a, int n) {
- if (n <= 0) {
+void bn_sqr_words(BN_ULONG *r, const BN_ULONG *a, size_t n) {
+ if (n == 0) {
return;
}
@@ -184,11 +185,11 @@
}
BN_ULONG bn_add_words(BN_ULONG *rp, const BN_ULONG *ap, const BN_ULONG *bp,
- int n) {
+ size_t n) {
BN_ULONG ret;
size_t i = 0;
- if (n <= 0) {
+ if (n == 0) {
return 0;
}
@@ -201,7 +202,8 @@
" adcq (%5,%2,8),%0 \n"
" movq %0,(%3,%2,8) \n"
" lea 1(%2),%2 \n"
- " loop 1b \n"
+ " dec %1 \n"
+ " jnz 1b \n"
" sbbq %0,%0 \n"
: "=&r"(ret), "+c"(n), "+r"(i)
: "r"(rp), "r"(ap), "r"(bp)
@@ -211,11 +213,11 @@
}
BN_ULONG bn_sub_words(BN_ULONG *rp, const BN_ULONG *ap, const BN_ULONG *bp,
- int n) {
+ size_t n) {
BN_ULONG ret;
size_t i = 0;
- if (n <= 0) {
+ if (n == 0) {
return 0;
}
@@ -228,7 +230,8 @@
" sbbq (%5,%2,8),%0 \n"
" movq %0,(%3,%2,8) \n"
" lea 1(%2),%2 \n"
- " loop 1b \n"
+ " dec %1 \n"
+ " jnz 1b \n"
" sbbq %0,%0 \n"
: "=&r"(ret), "+c"(n), "+r"(i)
: "r"(rp), "r"(ap), "r"(bp)
@@ -280,7 +283,7 @@
#define sqr_add_c2(a, i, j, c0, c1, c2) mul_add_c2((a)[i], (a)[j], c0, c1, c2)
-void bn_mul_comba8(BN_ULONG r[16], BN_ULONG a[8], BN_ULONG b[8]) {
+void bn_mul_comba8(BN_ULONG r[16], const BN_ULONG a[8], const BN_ULONG b[8]) {
BN_ULONG c1, c2, c3;
c1 = 0;
@@ -382,7 +385,7 @@
r[15] = c1;
}
-void bn_mul_comba4(BN_ULONG r[8], BN_ULONG a[4], BN_ULONG b[4]) {
+void bn_mul_comba4(BN_ULONG r[8], const BN_ULONG a[4], const BN_ULONG b[4]) {
BN_ULONG c1, c2, c3;
c1 = 0;
diff --git a/src/crypto/fipsmodule/bn/bn_test.cc b/src/crypto/fipsmodule/bn/bn_test.cc
index 975264e..8e156ad 100644
--- a/src/crypto/fipsmodule/bn/bn_test.cc
+++ b/src/crypto/fipsmodule/bn/bn_test.cc
@@ -93,6 +93,7 @@
#include <openssl/err.h>
#include <openssl/mem.h>
+#include "./internal.h"
#include "../../internal.h"
#include "../../test/file_test.h"
#include "../../test/test_util.h"
@@ -356,9 +357,11 @@
ASSERT_TRUE(BN_mul(ret.get(), a.get(), a.get(), ctx));
EXPECT_BIGNUMS_EQUAL("A * A", square.get(), ret.get());
- ASSERT_TRUE(BN_div(ret.get(), remainder.get(), square.get(), a.get(), ctx));
- EXPECT_BIGNUMS_EQUAL("Square / A", a.get(), ret.get());
- EXPECT_BIGNUMS_EQUAL("Square % A", zero.get(), remainder.get());
+ if (!BN_is_zero(a.get())) {
+ ASSERT_TRUE(BN_div(ret.get(), remainder.get(), square.get(), a.get(), ctx));
+ EXPECT_BIGNUMS_EQUAL("Square / A", a.get(), ret.get());
+ EXPECT_BIGNUMS_EQUAL("Square % A", zero.get(), remainder.get());
+ }
BN_set_negative(a.get(), 0);
ASSERT_TRUE(BN_sqrt(ret.get(), square.get(), ctx));
@@ -381,6 +384,31 @@
<< "BN_sqrt succeeded on a non-square";
ERR_clear_error();
}
+
+#if !defined(BORINGSSL_SHARED_LIBRARY)
+ if (static_cast<size_t>(a->top) <= BN_SMALL_MAX_WORDS) {
+ for (size_t num_a = a->top; num_a <= BN_SMALL_MAX_WORDS; num_a++) {
+ SCOPED_TRACE(num_a);
+ size_t num_r = 2 * num_a;
+ // Use newly-allocated buffers so ASan will catch out-of-bounds writes.
+ std::unique_ptr<BN_ULONG[]> a_words(new BN_ULONG[num_a]),
+ r_words(new BN_ULONG[num_r]);
+ OPENSSL_memset(a_words.get(), 0, num_a * sizeof(BN_ULONG));
+ OPENSSL_memcpy(a_words.get(), a->d, a->top * sizeof(BN_ULONG));
+
+ ASSERT_TRUE(bn_mul_small(r_words.get(), num_r, a_words.get(), num_a,
+ a_words.get(), num_a));
+ ASSERT_TRUE(bn_set_words(ret.get(), r_words.get(), num_r));
+ EXPECT_BIGNUMS_EQUAL("A * A (words)", square.get(), ret.get());
+
+ OPENSSL_memset(r_words.get(), 'A', num_r * sizeof(BN_ULONG));
+ ASSERT_TRUE(bn_sqr_small(r_words.get(), num_r, a_words.get(), num_a));
+
+ ASSERT_TRUE(bn_set_words(ret.get(), r_words.get(), num_r));
+ EXPECT_BIGNUMS_EQUAL("A^2 (words)", square.get(), ret.get());
+ }
+ }
+#endif
}
static void TestProduct(FileTest *t, BN_CTX *ctx) {
@@ -401,13 +429,46 @@
ASSERT_TRUE(BN_mul(ret.get(), a.get(), b.get(), ctx));
EXPECT_BIGNUMS_EQUAL("A * B", product.get(), ret.get());
- ASSERT_TRUE(BN_div(ret.get(), remainder.get(), product.get(), a.get(), ctx));
- EXPECT_BIGNUMS_EQUAL("Product / A", b.get(), ret.get());
- EXPECT_BIGNUMS_EQUAL("Product % A", zero.get(), remainder.get());
+ if (!BN_is_zero(a.get())) {
+ ASSERT_TRUE(
+ BN_div(ret.get(), remainder.get(), product.get(), a.get(), ctx));
+ EXPECT_BIGNUMS_EQUAL("Product / A", b.get(), ret.get());
+ EXPECT_BIGNUMS_EQUAL("Product % A", zero.get(), remainder.get());
+ }
- ASSERT_TRUE(BN_div(ret.get(), remainder.get(), product.get(), b.get(), ctx));
- EXPECT_BIGNUMS_EQUAL("Product / B", a.get(), ret.get());
- EXPECT_BIGNUMS_EQUAL("Product % B", zero.get(), remainder.get());
+ if (!BN_is_zero(b.get())) {
+ ASSERT_TRUE(
+ BN_div(ret.get(), remainder.get(), product.get(), b.get(), ctx));
+ EXPECT_BIGNUMS_EQUAL("Product / B", a.get(), ret.get());
+ EXPECT_BIGNUMS_EQUAL("Product % B", zero.get(), remainder.get());
+ }
+
+#if !defined(BORINGSSL_SHARED_LIBRARY)
+ if (!BN_is_negative(product.get()) &&
+ static_cast<size_t>(a->top) <= BN_SMALL_MAX_WORDS &&
+ static_cast<size_t>(b->top) <= BN_SMALL_MAX_WORDS) {
+ for (size_t num_a = a->top; num_a <= BN_SMALL_MAX_WORDS; num_a++) {
+ SCOPED_TRACE(num_a);
+ for (size_t num_b = b->top; num_b <= BN_SMALL_MAX_WORDS; num_b++) {
+ SCOPED_TRACE(num_b);
+ size_t num_r = num_a + num_b;
+ // Use newly-allocated buffers so ASan will catch out-of-bounds writes.
+ std::unique_ptr<BN_ULONG[]> a_words(new BN_ULONG[num_a]),
+ b_words(new BN_ULONG[num_b]), r_words(new BN_ULONG[num_r]);
+ OPENSSL_memset(a_words.get(), 0, num_a * sizeof(BN_ULONG));
+ OPENSSL_memcpy(a_words.get(), a->d, a->top * sizeof(BN_ULONG));
+
+ OPENSSL_memset(b_words.get(), 0, num_b * sizeof(BN_ULONG));
+ OPENSSL_memcpy(b_words.get(), b->d, b->top * sizeof(BN_ULONG));
+
+ ASSERT_TRUE(bn_mul_small(r_words.get(), num_r, a_words.get(), num_a,
+ b_words.get(), num_b));
+ ASSERT_TRUE(bn_set_words(ret.get(), r_words.get(), num_r));
+ EXPECT_BIGNUMS_EQUAL("A * B (words)", product.get(), ret.get());
+ }
+ }
+ }
+#endif
}
static void TestQuotient(FileTest *t, BN_CTX *ctx) {
@@ -481,15 +542,39 @@
ASSERT_TRUE(a_tmp);
ASSERT_TRUE(b_tmp);
ASSERT_TRUE(BN_MONT_CTX_set(mont.get(), m.get(), ctx));
- ASSERT_TRUE(BN_nnmod(a_tmp.get(), a.get(), m.get(), ctx));
- ASSERT_TRUE(BN_nnmod(b_tmp.get(), b.get(), m.get(), ctx));
- ASSERT_TRUE(BN_to_montgomery(a_tmp.get(), a_tmp.get(), mont.get(), ctx));
- ASSERT_TRUE(BN_to_montgomery(b_tmp.get(), b_tmp.get(), mont.get(), ctx));
+ ASSERT_TRUE(BN_nnmod(a.get(), a.get(), m.get(), ctx));
+ ASSERT_TRUE(BN_nnmod(b.get(), b.get(), m.get(), ctx));
+ ASSERT_TRUE(BN_to_montgomery(a_tmp.get(), a.get(), mont.get(), ctx));
+ ASSERT_TRUE(BN_to_montgomery(b_tmp.get(), b.get(), mont.get(), ctx));
ASSERT_TRUE(BN_mod_mul_montgomery(ret.get(), a_tmp.get(), b_tmp.get(),
mont.get(), ctx));
ASSERT_TRUE(BN_from_montgomery(ret.get(), ret.get(), mont.get(), ctx));
EXPECT_BIGNUMS_EQUAL("A * B (mod M) (Montgomery)", mod_mul.get(),
ret.get());
+
+#if !defined(BORINGSSL_SHARED_LIBRARY)
+ if (m->top <= BN_SMALL_MAX_WORDS) {
+ std::unique_ptr<BN_ULONG[]> a_words(new BN_ULONG[m->top]),
+ b_words(new BN_ULONG[m->top]), r_words(new BN_ULONG[m->top]);
+ OPENSSL_memset(a_words.get(), 0, m->top * sizeof(BN_ULONG));
+ OPENSSL_memcpy(a_words.get(), a->d, a->top * sizeof(BN_ULONG));
+ OPENSSL_memset(b_words.get(), 0, m->top * sizeof(BN_ULONG));
+ OPENSSL_memcpy(b_words.get(), b->d, b->top * sizeof(BN_ULONG));
+ ASSERT_TRUE(bn_to_montgomery_small(a_words.get(), m->top, a_words.get(),
+ m->top, mont.get()));
+ ASSERT_TRUE(bn_to_montgomery_small(b_words.get(), m->top, b_words.get(),
+ m->top, mont.get()));
+ ASSERT_TRUE(bn_mod_mul_montgomery_small(
+ r_words.get(), m->top, a_words.get(), m->top, b_words.get(), m->top,
+ mont.get()));
+ // Use the second half of |tmp| so ASan will catch out-of-bounds writes.
+ ASSERT_TRUE(bn_from_montgomery_small(r_words.get(), m->top, r_words.get(),
+ m->top, mont.get()));
+ ASSERT_TRUE(bn_set_words(ret.get(), r_words.get(), m->top));
+ EXPECT_BIGNUMS_EQUAL("A * B (mod M) (Montgomery, words)", mod_mul.get(),
+ ret.get());
+ }
+#endif
}
}
@@ -520,8 +605,8 @@
ASSERT_TRUE(mont);
ASSERT_TRUE(a_tmp);
ASSERT_TRUE(BN_MONT_CTX_set(mont.get(), m.get(), ctx));
- ASSERT_TRUE(BN_nnmod(a_tmp.get(), a.get(), m.get(), ctx));
- ASSERT_TRUE(BN_to_montgomery(a_tmp.get(), a_tmp.get(), mont.get(), ctx));
+ ASSERT_TRUE(BN_nnmod(a.get(), a.get(), m.get(), ctx));
+ ASSERT_TRUE(BN_to_montgomery(a_tmp.get(), a.get(), mont.get(), ctx));
ASSERT_TRUE(BN_mod_mul_montgomery(ret.get(), a_tmp.get(), a_tmp.get(),
mont.get(), ctx));
ASSERT_TRUE(BN_from_montgomery(ret.get(), ret.get(), mont.get(), ctx));
@@ -535,6 +620,38 @@
ASSERT_TRUE(BN_from_montgomery(ret.get(), ret.get(), mont.get(), ctx));
EXPECT_BIGNUMS_EQUAL("A * A_copy (mod M) (Montgomery)", mod_square.get(),
ret.get());
+
+#if !defined(BORINGSSL_SHARED_LIBRARY)
+ if (m->top <= BN_SMALL_MAX_WORDS) {
+ std::unique_ptr<BN_ULONG[]> a_words(new BN_ULONG[m->top]),
+ a_copy_words(new BN_ULONG[m->top]), r_words(new BN_ULONG[m->top]);
+ OPENSSL_memset(a_words.get(), 0, m->top * sizeof(BN_ULONG));
+ OPENSSL_memcpy(a_words.get(), a->d, a->top * sizeof(BN_ULONG));
+ ASSERT_TRUE(bn_to_montgomery_small(a_words.get(), m->top, a_words.get(),
+ m->top, mont.get()));
+ ASSERT_TRUE(bn_mod_mul_montgomery_small(
+ r_words.get(), m->top, a_words.get(), m->top, a_words.get(), m->top,
+ mont.get()));
+ ASSERT_TRUE(bn_from_montgomery_small(r_words.get(), m->top, r_words.get(),
+ m->top, mont.get()));
+ ASSERT_TRUE(bn_set_words(ret.get(), r_words.get(), m->top));
+ EXPECT_BIGNUMS_EQUAL("A * A (mod M) (Montgomery, words)",
+ mod_square.get(), ret.get());
+
+ // Repeat the operation with |a_copy_words|.
+ OPENSSL_memcpy(a_copy_words.get(), a_words.get(),
+ m->top * sizeof(BN_ULONG));
+ ASSERT_TRUE(bn_mod_mul_montgomery_small(
+ r_words.get(), m->top, a_words.get(), m->top, a_copy_words.get(),
+ m->top, mont.get()));
+ // Use the second half of |tmp| so ASan will catch out-of-bounds writes.
+ ASSERT_TRUE(bn_from_montgomery_small(r_words.get(), m->top, r_words.get(),
+ m->top, mont.get()));
+ ASSERT_TRUE(bn_set_words(ret.get(), r_words.get(), m->top));
+ EXPECT_BIGNUMS_EQUAL("A * A_copy (mod M) (Montgomery, words)",
+ mod_square.get(), ret.get());
+ }
+#endif
}
}
@@ -563,6 +680,28 @@
ctx, NULL));
EXPECT_BIGNUMS_EQUAL("A ^ E (mod M) (constant-time)", mod_exp.get(),
ret.get());
+
+#if !defined(BORINGSSL_SHARED_LIBRARY)
+ if (m->top <= BN_SMALL_MAX_WORDS) {
+ bssl::UniquePtr<BN_MONT_CTX> mont(BN_MONT_CTX_new());
+ ASSERT_TRUE(mont.get());
+ ASSERT_TRUE(BN_MONT_CTX_set(mont.get(), m.get(), ctx));
+ ASSERT_TRUE(BN_nnmod(a.get(), a.get(), m.get(), ctx));
+ std::unique_ptr<BN_ULONG[]> r_words(new BN_ULONG[m->top]),
+ a_words(new BN_ULONG[m->top]);
+ OPENSSL_memset(a_words.get(), 0, m->top * sizeof(BN_ULONG));
+ OPENSSL_memcpy(a_words.get(), a->d, a->top * sizeof(BN_ULONG));
+ ASSERT_TRUE(bn_to_montgomery_small(a_words.get(), m->top, a_words.get(),
+ m->top, mont.get()));
+ ASSERT_TRUE(bn_mod_exp_mont_small(r_words.get(), m->top, a_words.get(),
+ m->top, e->d, e->top, mont.get()));
+ ASSERT_TRUE(bn_from_montgomery_small(r_words.get(), m->top, r_words.get(),
+ m->top, mont.get()));
+ ASSERT_TRUE(bn_set_words(ret.get(), r_words.get(), m->top));
+ EXPECT_BIGNUMS_EQUAL("A ^ E (mod M) (Montgomery, words)", mod_exp.get(),
+ ret.get());
+ }
+#endif
}
}
@@ -1618,3 +1757,94 @@
nullptr /* callback */));
EXPECT_EQ(0, is_probably_prime_2);
}
+
+#if !defined(BORINGSSL_SHARED_LIBRARY)
+TEST_F(BNTest, LessThanWords) {
+ // kTestVectors is an array of 256-bit values in sorted order.
+ static const BN_ULONG kTestVectors[][256 / BN_BITS2] = {
+ {TOBN(0x00000000, 0x00000000), TOBN(0x00000000, 0x00000000),
+ TOBN(0x00000000, 0x00000000), TOBN(0x00000000, 0x00000000)},
+ {TOBN(0x00000000, 0x00000001), TOBN(0x00000000, 0x00000000),
+ TOBN(0x00000000, 0x00000000), TOBN(0x00000000, 0x00000000)},
+ {TOBN(0x00000000, 0x00000002), TOBN(0x00000000, 0x00000000),
+ TOBN(0x00000000, 0x00000000), TOBN(0x00000000, 0x00000000)},
+ {TOBN(0x00000000, 0x0000ffff), TOBN(0x00000000, 0x00000000),
+ TOBN(0x00000000, 0x00000000), TOBN(0x00000000, 0x00000000)},
+ {TOBN(0x00000000, 0x83339914), TOBN(0x00000000, 0x00000000),
+ TOBN(0x00000000, 0x00000000), TOBN(0x00000000, 0x00000000)},
+ {TOBN(0x00000000, 0xfffffffe), TOBN(0x00000000, 0x00000000),
+ TOBN(0x00000000, 0x00000000), TOBN(0x00000000, 0x00000000)},
+ {TOBN(0x00000000, 0xffffffff), TOBN(0x00000000, 0x00000000),
+ TOBN(0x00000000, 0x00000000), TOBN(0x00000000, 0x00000000)},
+ {TOBN(0xed17ac85, 0x83339914), TOBN(0x00000000, 0x00000000),
+ TOBN(0x00000000, 0x00000000), TOBN(0x00000000, 0x00000000)},
+ {TOBN(0xffffffff, 0xffffffff), TOBN(0x00000000, 0x00000000),
+ TOBN(0x00000000, 0x00000000), TOBN(0x00000000, 0x00000000)},
+ {TOBN(0x00000000, 0x83339914), TOBN(0x00000000, 0x00000001),
+ TOBN(0x00000000, 0x00000000), TOBN(0x00000000, 0x00000000)},
+ {TOBN(0xffffffff, 0xffffffff), TOBN(0xffffffff, 0xffffffff),
+ TOBN(0x00000000, 0x00000000), TOBN(0x00000000, 0x00000000)},
+ {TOBN(0xffffffff, 0xffffffff), TOBN(0xffffffff, 0xffffffff),
+ TOBN(0xffffffff, 0xffffffff), TOBN(0x00000000, 0x00000000)},
+ {TOBN(0x00000000, 0x00000000), TOBN(0x1d6f60ba, 0x893ba84c),
+ TOBN(0x597d89b3, 0x754abe9f), TOBN(0xb504f333, 0xf9de6484)},
+ {TOBN(0x00000000, 0x83339915), TOBN(0x1d6f60ba, 0x893ba84c),
+ TOBN(0x597d89b3, 0x754abe9f), TOBN(0xb504f333, 0xf9de6484)},
+ {TOBN(0xed17ac85, 0x00000000), TOBN(0x1d6f60ba, 0x893ba84c),
+ TOBN(0x597d89b3, 0x754abe9f), TOBN(0xb504f333, 0xf9de6484)},
+ {TOBN(0xed17ac85, 0x83339915), TOBN(0x1d6f60ba, 0x893ba84c),
+ TOBN(0x597d89b3, 0x754abe9f), TOBN(0xb504f333, 0xf9de6484)},
+ {TOBN(0xed17ac85, 0xffffffff), TOBN(0x1d6f60ba, 0x893ba84c),
+ TOBN(0x597d89b3, 0x754abe9f), TOBN(0xb504f333, 0xf9de6484)},
+ {TOBN(0xffffffff, 0x83339915), TOBN(0x1d6f60ba, 0x893ba84c),
+ TOBN(0x597d89b3, 0x754abe9f), TOBN(0xb504f333, 0xf9de6484)},
+ {TOBN(0xffffffff, 0xffffffff), TOBN(0x1d6f60ba, 0x893ba84c),
+ TOBN(0x597d89b3, 0x754abe9f), TOBN(0xb504f333, 0xf9de6484)},
+ {TOBN(0x00000000, 0x00000000), TOBN(0x00000000, 0x00000000),
+ TOBN(0x00000000, 0x00000000), TOBN(0xffffffff, 0xffffffff)},
+ {TOBN(0x00000000, 0x00000000), TOBN(0x00000000, 0x00000000),
+ TOBN(0xffffffff, 0xffffffff), TOBN(0xffffffff, 0xffffffff)},
+ {TOBN(0x00000000, 0x00000001), TOBN(0x00000000, 0x00000000),
+ TOBN(0xffffffff, 0xffffffff), TOBN(0xffffffff, 0xffffffff)},
+ {TOBN(0x00000000, 0x00000000), TOBN(0xffffffff, 0xffffffff),
+ TOBN(0xffffffff, 0xffffffff), TOBN(0xffffffff, 0xffffffff)},
+ {TOBN(0xffffffff, 0xffffffff), TOBN(0xffffffff, 0xffffffff),
+ TOBN(0xffffffff, 0xffffffff), TOBN(0xffffffff, 0xffffffff)},
+ };
+
+ // Determine where the single-word values stop.
+ size_t one_word;
+ for (one_word = 0; one_word < OPENSSL_ARRAY_SIZE(kTestVectors); one_word++) {
+ int is_word = 1;
+ for (size_t i = 1; i < OPENSSL_ARRAY_SIZE(kTestVectors[one_word]); i++) {
+ if (kTestVectors[one_word][i] != 0) {
+ is_word = 0;
+ break;
+ }
+ }
+ if (!is_word) {
+ break;
+ }
+ }
+
+ for (size_t i = 0; i < OPENSSL_ARRAY_SIZE(kTestVectors); i++) {
+ SCOPED_TRACE(i);
+ for (size_t j = 0; j < OPENSSL_ARRAY_SIZE(kTestVectors); j++) {
+ SCOPED_TRACE(j);
+ EXPECT_EQ(i < j ? 1 : 0,
+ bn_less_than_words(kTestVectors[i], kTestVectors[j],
+ OPENSSL_ARRAY_SIZE(kTestVectors[i])));
+ for (size_t k = 0; k < one_word; k++) {
+ SCOPED_TRACE(k);
+ EXPECT_EQ(k <= i && i < j ? 1 : 0,
+ bn_in_range_words(kTestVectors[i], kTestVectors[k][0],
+ kTestVectors[j],
+ OPENSSL_ARRAY_SIZE(kTestVectors[i])));
+ }
+ }
+ }
+
+ EXPECT_EQ(0, bn_less_than_words(NULL, NULL, 0));
+ EXPECT_EQ(0, bn_in_range_words(NULL, 0, NULL, 0));
+}
+#endif // !BORINGSSL_SHARED_LIBRARY
diff --git a/src/crypto/fipsmodule/bn/bn_tests.txt b/src/crypto/fipsmodule/bn/bn_tests.txt
index f809e7e..eb447b5 100644
--- a/src/crypto/fipsmodule/bn/bn_tests.txt
+++ b/src/crypto/fipsmodule/bn/bn_tests.txt
@@ -5349,6 +5349,12 @@
Square = eea8028b26e0df090504d54da714a6f5f2695202e53cff479c78aedd47a8dc676243ec586740fde53b3eca9ca02b91031ce766242184109503fbe25b1b6d318e3cd5970fabd16dfa22984dd2e9f1e0f14c189170fc69c031d66663703e6235a942d51a4545bd7b0769d01d302ce2b00b83f01568a1e378f61fd0ca6201b0490330580cd9de85719e174a71915d7efbf65cd73d8f4e66f27e0dd3144d58ec09ed0f7ed7d1238ee596922807100fb7a11127944ddcdec6a9ca3bbf6df7301e354f3f049bfb7c275b43c3d8cda5907a932fba507c9145ea3166081c1b48fcc710ee32cd931f936c796b14f8a78a592e67753a7c9e428a01719c8ba82652f3a89fae110
A = -3dcb44be1e54c5a5d7db48055ca9afa1ebe2ae648aa6e16ac497502a7deee09ffa124720fad0ab163ce8b3ea6a90f110ea52b67dbc424d0cf1e8c9726dfd9e45bebcefaa5cd5706edeed27896525f31c6bbea3d67ee97badefabf3e2532470b66e3ae3100f66ddf50cf02fc3a8e3f44c304251d3b6a7ca3a6e4bd5d16a41bd97a4
+Square = 0
+A = 0
+
+Square = 1
+A = 1
+
# Product tests.
#
@@ -5954,6 +5960,22 @@
A = a57da276998c548101f514e9f
B = -542fb814f45924aa09a16f2a6
+Product = 0
+A = 0
+B = 542fb814f45924aa09a16f2a6
+
+Product = 0
+A = 542fb814f45924aa09a16f2a6
+B = 0
+
+Product = 542fb814f45924aa09a16f2a6
+A = 1
+B = 542fb814f45924aa09a16f2a6
+
+Product = 542fb814f45924aa09a16f2a6
+A = 542fb814f45924aa09a16f2a6
+B = 1
+
# Quotient tests.
#
diff --git a/src/crypto/fipsmodule/bn/cmp.c b/src/crypto/fipsmodule/bn/cmp.c
index 7864707..acc017f 100644
--- a/src/crypto/fipsmodule/bn/cmp.c
+++ b/src/crypto/fipsmodule/bn/cmp.c
@@ -57,8 +57,10 @@
#include <openssl/bn.h>
#include <openssl/mem.h>
+#include <openssl/type_check.h>
#include "internal.h"
+#include "../../internal.h"
int BN_ucmp(const BIGNUM *a, const BIGNUM *b) {
@@ -174,6 +176,19 @@
return bn_cmp_words(a, b, cl);
}
+int bn_less_than_words(const BN_ULONG *a, const BN_ULONG *b, size_t len) {
+ OPENSSL_COMPILE_ASSERT(sizeof(BN_ULONG) <= sizeof(crypto_word_t),
+ crypto_word_t_too_small);
+ int ret = 0;
+ // Process the words in little-endian order.
+ for (size_t i = 0; i < len; i++) {
+ crypto_word_t eq = constant_time_eq_w(a[i], b[i]);
+ crypto_word_t lt = constant_time_lt_w(a[i], b[i]);
+ ret = constant_time_select_int(eq, ret, constant_time_select_int(lt, 1, 0));
+ }
+ return ret;
+}
+
int BN_abs_is_word(const BIGNUM *bn, BN_ULONG w) {
switch (bn->top) {
case 1:
diff --git a/src/crypto/fipsmodule/bn/exponentiation.c b/src/crypto/fipsmodule/bn/exponentiation.c
index 2d40e8f..a5cb7da 100644
--- a/src/crypto/fipsmodule/bn/exponentiation.c
+++ b/src/crypto/fipsmodule/bn/exponentiation.c
@@ -434,6 +434,15 @@
// value returned from |BN_window_bits_for_exponent_size|.
#define TABLE_SIZE 32
+// TABLE_BITS_SMALL is the smallest value returned from
+// |BN_window_bits_for_exponent_size| when |b| is at most |BN_BITS2| *
+// |BN_SMALL_MAX_WORDS| words.
+#define TABLE_BITS_SMALL 5
+
+// TABLE_SIZE_SMALL is the same as |TABLE_SIZE|, but when |b| is at most
+// |BN_BITS2| * |BN_SMALL_MAX_WORDS|.
+#define TABLE_SIZE_SMALL (1 << (TABLE_BITS_SMALL - 1))
+
static int mod_exp_recp(BIGNUM *r, const BIGNUM *a, const BIGNUM *p,
const BIGNUM *m, BN_CTX *ctx) {
int i, j, bits, ret = 0, wstart, window;
@@ -734,6 +743,152 @@
return ret;
}
+int bn_mod_exp_mont_small(BN_ULONG *r, size_t num_r, const BN_ULONG *a,
+ size_t num_a, const BN_ULONG *p, size_t num_p,
+ const BN_MONT_CTX *mont) {
+ const BN_ULONG *n = mont->N.d;
+ size_t num_n = mont->N.top;
+ if (num_n != num_a || num_n != num_r || num_n > BN_SMALL_MAX_WORDS) {
+ OPENSSL_PUT_ERROR(BN, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
+ return 0;
+ }
+ if (!BN_is_odd(&mont->N)) {
+ OPENSSL_PUT_ERROR(BN, BN_R_CALLED_WITH_EVEN_MODULUS);
+ return 0;
+ }
+ unsigned bits = 0;
+ if (num_p != 0) {
+ bits = BN_num_bits_word(p[num_p - 1]) + (num_p - 1) * BN_BITS2;
+ }
+ if (bits == 0) {
+ OPENSSL_memset(r, 0, num_r * sizeof(BN_ULONG));
+ if (!BN_is_one(&mont->N)) {
+ r[0] = 1;
+ }
+ return 1;
+ }
+
+ // We exponentiate by looking at sliding windows of the exponent and
+ // precomputing powers of |a|. Windows may be shifted so they always end on a
+ // set bit, so only precompute odd powers. We compute val[i] = a^(2*i + 1) for
+ // i = 0 to 2^(window-1), all in Montgomery form.
+ unsigned window = BN_window_bits_for_exponent_size(bits);
+ if (window > TABLE_BITS_SMALL) {
+ window = TABLE_BITS_SMALL; // Tolerate excessively large |p|.
+ }
+ int ret = 0;
+ BN_ULONG val[TABLE_SIZE_SMALL][BN_SMALL_MAX_WORDS];
+ OPENSSL_memcpy(val[0], a, num_n * sizeof(BN_ULONG));
+ if (window > 1) {
+ BN_ULONG d[BN_SMALL_MAX_WORDS];
+ if (!bn_mod_mul_montgomery_small(d, num_n, val[0], num_n, val[0], num_n,
+ mont)) {
+ goto err;
+ }
+ for (unsigned i = 1; i < 1u << (window - 1); i++) {
+ if (!bn_mod_mul_montgomery_small(val[i], num_n, val[i - 1], num_n, d,
+ num_n, mont)) {
+ goto err;
+ }
+ }
+ }
+
+ // Set |r| to one in Montgomery form. If the high bit of |m| is set, |m| is
+ // close to R and we subtract rather than perform Montgomery reduction.
+ if (n[num_n - 1] & (((BN_ULONG)1) << (BN_BITS2 - 1))) {
+ // r = 2^(top*BN_BITS2) - m
+ r[0] = 0 - n[0];
+ for (size_t i = 1; i < num_n; i++) {
+ r[i] = ~n[i];
+ }
+ } else if (!bn_from_montgomery_small(r, num_r, mont->RR.d, mont->RR.top,
+ mont)) {
+ goto err;
+ }
+
+ int r_is_one = 1;
+ unsigned wstart = bits - 1; // The top bit of the window.
+ for (;;) {
+ if (!bn_is_bit_set_words(p, num_p, wstart)) {
+ if (!r_is_one &&
+ !bn_mod_mul_montgomery_small(r, num_r, r, num_r, r, num_r, mont)) {
+ goto err;
+ }
+ if (wstart == 0) {
+ break;
+ }
+ wstart--;
+ continue;
+ }
+
+ // We now have wstart on a set bit. Find the largest window we can use.
+ unsigned wvalue = 1;
+ unsigned wsize = 0;
+ for (unsigned i = 1; i < window && i <= wstart; i++) {
+ if (bn_is_bit_set_words(p, num_p, wstart - i)) {
+ wvalue <<= (i - wsize);
+ wvalue |= 1;
+ wsize = i;
+ }
+ }
+
+ // Shift |r| to the end of the window.
+ if (!r_is_one) {
+ for (unsigned i = 0; i < wsize + 1; i++) {
+ if (!bn_mod_mul_montgomery_small(r, num_r, r, num_r, r, num_r, mont)) {
+ goto err;
+ }
+ }
+ }
+
+ assert(wvalue & 1);
+ assert(wvalue < (1u << window));
+ if (!bn_mod_mul_montgomery_small(r, num_r, r, num_r, val[wvalue >> 1],
+ num_n, mont)) {
+ goto err;
+ }
+
+ r_is_one = 0;
+ if (wstart == wsize) {
+ break;
+ }
+ wstart -= wsize + 1;
+ }
+
+ ret = 1;
+
+err:
+ OPENSSL_cleanse(val, sizeof(val));
+ return ret;
+}
+
+int bn_mod_inverse_prime_mont_small(BN_ULONG *r, size_t num_r,
+ const BN_ULONG *a, size_t num_a,
+ const BN_MONT_CTX *mont) {
+ const BN_ULONG *p = mont->N.d;
+ size_t num_p = mont->N.top;
+ if (num_p > BN_SMALL_MAX_WORDS || num_p == 0) {
+ OPENSSL_PUT_ERROR(BN, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
+ return 0;
+ }
+
+ // Per Fermat's Little Theorem, a^-1 = a^(p-2) (mod p) for p prime.
+ BN_ULONG p_minus_two[BN_SMALL_MAX_WORDS];
+ OPENSSL_memcpy(p_minus_two, p, num_p * sizeof(BN_ULONG));
+ if (p_minus_two[0] >= 2) {
+ p_minus_two[0] -= 2;
+ } else {
+ p_minus_two[0] -= 2;
+ for (size_t i = 1; i < num_p; i++) {
+ if (p_minus_two[i]-- != 0) {
+ break;
+ }
+ }
+ }
+
+ return bn_mod_exp_mont_small(r, num_r, a, num_a, p_minus_two, num_p, mont);
+}
+
// |BN_mod_exp_mont_consttime| stores the precomputed powers in a specific
// layout so that accessing any of these table values shows the same access
diff --git a/src/crypto/fipsmodule/bn/generic.c b/src/crypto/fipsmodule/bn/generic.c
index 44e0f2c..a39a033 100644
--- a/src/crypto/fipsmodule/bn/generic.c
+++ b/src/crypto/fipsmodule/bn/generic.c
@@ -124,12 +124,11 @@
#endif // !BN_ULLONG
-BN_ULONG bn_mul_add_words(BN_ULONG *rp, const BN_ULONG *ap, int num,
+BN_ULONG bn_mul_add_words(BN_ULONG *rp, const BN_ULONG *ap, size_t num,
BN_ULONG w) {
BN_ULONG c1 = 0;
- assert(num >= 0);
- if (num <= 0) {
+ if (num == 0) {
return c1;
}
@@ -153,11 +152,11 @@
return c1;
}
-BN_ULONG bn_mul_words(BN_ULONG *rp, const BN_ULONG *ap, int num, BN_ULONG w) {
+BN_ULONG bn_mul_words(BN_ULONG *rp, const BN_ULONG *ap, size_t num,
+ BN_ULONG w) {
BN_ULONG c1 = 0;
- assert(num >= 0);
- if (num <= 0) {
+ if (num == 0) {
return c1;
}
@@ -179,9 +178,8 @@
return c1;
}
-void bn_sqr_words(BN_ULONG *r, const BN_ULONG *a, int n) {
- assert(n >= 0);
- if (n <= 0) {
+void bn_sqr_words(BN_ULONG *r, const BN_ULONG *a, size_t n) {
+ if (n == 0) {
return;
}
@@ -204,11 +202,10 @@
#ifdef BN_ULLONG
BN_ULONG bn_add_words(BN_ULONG *r, const BN_ULONG *a, const BN_ULONG *b,
- int n) {
+ size_t n) {
BN_ULLONG ll = 0;
- assert(n >= 0);
- if (n <= 0) {
+ if (n == 0) {
return 0;
}
@@ -245,11 +242,10 @@
#else // !BN_ULLONG
BN_ULONG bn_add_words(BN_ULONG *r, const BN_ULONG *a, const BN_ULONG *b,
- int n) {
+ size_t n) {
BN_ULONG c, l, t;
- assert(n >= 0);
- if (n <= 0) {
+ if (n == 0) {
return (BN_ULONG)0;
}
@@ -302,12 +298,11 @@
#endif // !BN_ULLONG
BN_ULONG bn_sub_words(BN_ULONG *r, const BN_ULONG *a, const BN_ULONG *b,
- int n) {
+ size_t n) {
BN_ULONG t1, t2;
int c = 0;
- assert(n >= 0);
- if (n <= 0) {
+ if (n == 0) {
return (BN_ULONG)0;
}
@@ -458,7 +453,7 @@
#endif // !BN_ULLONG
-void bn_mul_comba8(BN_ULONG r[16], BN_ULONG a[8], BN_ULONG b[8]) {
+void bn_mul_comba8(BN_ULONG r[16], const BN_ULONG a[8], const BN_ULONG b[8]) {
BN_ULONG c1, c2, c3;
c1 = 0;
@@ -560,7 +555,7 @@
r[15] = c1;
}
-void bn_mul_comba4(BN_ULONG r[8], BN_ULONG a[4], BN_ULONG b[4]) {
+void bn_mul_comba4(BN_ULONG r[8], const BN_ULONG a[4], const BN_ULONG b[4]) {
BN_ULONG c1, c2, c3;
c1 = 0;
diff --git a/src/crypto/fipsmodule/bn/internal.h b/src/crypto/fipsmodule/bn/internal.h
index acc0ac8..75efbfa 100644
--- a/src/crypto/fipsmodule/bn/internal.h
+++ b/src/crypto/fipsmodule/bn/internal.h
@@ -214,40 +214,40 @@
// the result in |rp|. |ap| and |rp| must both be |num| words long. It returns
// the carry word of the operation. |ap| and |rp| may be equal but otherwise may
// not alias.
-BN_ULONG bn_mul_add_words(BN_ULONG *rp, const BN_ULONG *ap, int num,
+BN_ULONG bn_mul_add_words(BN_ULONG *rp, const BN_ULONG *ap, size_t num,
BN_ULONG w);
// bn_mul_words multiples |ap| by |w| and places the result in |rp|. |ap| and
// |rp| must both be |num| words long. It returns the carry word of the
// operation. |ap| and |rp| may be equal but otherwise may not alias.
-BN_ULONG bn_mul_words(BN_ULONG *rp, const BN_ULONG *ap, int num, BN_ULONG w);
+BN_ULONG bn_mul_words(BN_ULONG *rp, const BN_ULONG *ap, size_t num, BN_ULONG w);
// bn_sqr_words sets |rp[2*i]| and |rp[2*i+1]| to |ap[i]|'s square, for all |i|
// up to |num|. |ap| is an array of |num| words and |rp| an array of |2*num|
// words. |ap| and |rp| may not alias.
//
// This gives the contribution of the |ap[i]*ap[i]| terms when squaring |ap|.
-void bn_sqr_words(BN_ULONG *rp, const BN_ULONG *ap, int num);
+void bn_sqr_words(BN_ULONG *rp, const BN_ULONG *ap, size_t num);
// bn_add_words adds |ap| to |bp| and places the result in |rp|, each of which
// are |num| words long. It returns the carry bit, which is one if the operation
// overflowed and zero otherwise. Any pair of |ap|, |bp|, and |rp| may be equal
// to each other but otherwise may not alias.
BN_ULONG bn_add_words(BN_ULONG *rp, const BN_ULONG *ap, const BN_ULONG *bp,
- int num);
+ size_t num);
// bn_sub_words subtracts |bp| from |ap| and places the result in |rp|. It
// returns the borrow bit, which is one if the computation underflowed and zero
// otherwise. Any pair of |ap|, |bp|, and |rp| may be equal to each other but
// otherwise may not alias.
BN_ULONG bn_sub_words(BN_ULONG *rp, const BN_ULONG *ap, const BN_ULONG *bp,
- int num);
+ size_t num);
// bn_mul_comba4 sets |r| to the product of |a| and |b|.
-void bn_mul_comba4(BN_ULONG r[8], BN_ULONG a[4], BN_ULONG b[4]);
+void bn_mul_comba4(BN_ULONG r[8], const BN_ULONG a[4], const BN_ULONG b[4]);
// bn_mul_comba8 sets |r| to the product of |a| and |b|.
-void bn_mul_comba8(BN_ULONG r[16], BN_ULONG a[8], BN_ULONG b[8]);
+void bn_mul_comba8(BN_ULONG r[16], const BN_ULONG a[8], const BN_ULONG b[8]);
// bn_sqr_comba8 sets |r| to |a|^2.
void bn_sqr_comba8(BN_ULONG r[16], const BN_ULONG a[4]);
@@ -265,6 +265,29 @@
// the length of |a| minus the length of |b|.
int bn_cmp_part_words(const BN_ULONG *a, const BN_ULONG *b, int cl, int dl);
+// bn_less_than_words returns one if |a| < |b| and zero otherwise, where |a|
+// and |b| both are |len| words long. It runs in constant time.
+int bn_less_than_words(const BN_ULONG *a, const BN_ULONG *b, size_t len);
+
+// bn_in_range_words returns one if |min_inclusive| <= |a| < |max_exclusive|,
+// where |a| and |max_exclusive| both are |len| words long. This function leaks
+// which of [0, min_inclusive), [min_inclusive, max_exclusive), and
+// [max_exclusive, 2^(BN_BITS2*len)) contains |a|, but otherwise the value of
+// |a| is secret.
+int bn_in_range_words(const BN_ULONG *a, BN_ULONG min_inclusive,
+ const BN_ULONG *max_exclusive, size_t len);
+
+// bn_rand_range_words sets |out| to a uniformly distributed random number from
+// |min_inclusive| to |max_exclusive|. Both |out| and |max_exclusive| are |len|
+// words long.
+//
+// This function runs in time independent of the result, but |min_inclusive| and
+// |max_exclusive| are public data. (Information about the range is unavoidably
+// leaked by how many iterations it took to select a number.)
+int bn_rand_range_words(BN_ULONG *out, BN_ULONG min_inclusive,
+ const BN_ULONG *max_exclusive, size_t len,
+ const uint8_t additional_data[32]);
+
int bn_mul_mont(BN_ULONG *rp, const BN_ULONG *ap, const BN_ULONG *bp,
const BN_ULONG *np, const BN_ULONG *n0, int num);
@@ -295,6 +318,93 @@
// -2 on error.
int bn_jacobi(const BIGNUM *a, const BIGNUM *b, BN_CTX *ctx);
+// bn_is_bit_set_words returns one if bit |bit| is set in |a| and zero
+// otherwise.
+int bn_is_bit_set_words(const BN_ULONG *a, size_t num, unsigned bit);
+
+
+// Low-level operations for small numbers.
+//
+// The following functions implement algorithms suitable for use with scalars
+// and field elements in elliptic curves. They rely on the number being small
+// both to stack-allocate various temporaries and because they do not implement
+// optimizations useful for the larger values used in RSA.
+
+// BN_SMALL_MAX_WORDS is the largest size input these functions handle. This
+// limit allows temporaries to be more easily stack-allocated. This limit is set
+// to accommodate P-521.
+#if defined(OPENSSL_32_BIT)
+#define BN_SMALL_MAX_WORDS 17
+#else
+#define BN_SMALL_MAX_WORDS 9
+#endif
+
+// bn_mul_small sets |r| to |a|*|b|. |num_r| must be |num_a| + |num_b|. |r| may
+// not alias with |a| or |b|. This function returns one on success and zero if
+// lengths are inconsistent.
+int bn_mul_small(BN_ULONG *r, size_t num_r, const BN_ULONG *a, size_t num_a,
+ const BN_ULONG *b, size_t num_b);
+
+// bn_sqr_small sets |r| to |a|^2. |num_a| must be at most |BN_SMALL_MAX_WORDS|.
+// |num_r| must be |num_a|*2. |r| and |a| may not alias. This function returns
+// one on success and zero on programmer error.
+int bn_sqr_small(BN_ULONG *r, size_t num_r, const BN_ULONG *a, size_t num_a);
+
+// In the following functions, the modulus must be at most |BN_SMALL_MAX_WORDS|
+// words long.
+
+// bn_to_montgomery_small sets |r| to |a| translated to the Montgomery domain.
+// |num_a| and |num_r| must be the length of the modulus, which is
+// |mont->N.top|. |a| must be fully reduced. This function returns one on
+// success and zero if lengths are inconsistent. |r| and |a| may alias.
+int bn_to_montgomery_small(BN_ULONG *r, size_t num_r, const BN_ULONG *a,
+ size_t num_a, const BN_MONT_CTX *mont);
+
+// bn_from_montgomery_small sets |r| to |a| translated out of the Montgomery
+// domain. |num_r| must be the length of the modulus, which is |mont->N.top|.
+// |a| must be at most |mont->N.top| * R and |num_a| must be at most 2 *
+// |mont->N.top|. This function returns one on success and zero if lengths are
+// inconsistent. |r| and |a| may alias.
+int bn_from_montgomery_small(BN_ULONG *r, size_t num_r, const BN_ULONG *a,
+ size_t num_a, const BN_MONT_CTX *mont);
+
+// bn_mod_mul_montgomery_small sets |r| to |a| * |b| mod |mont->N|. Both inputs
+// and outputs are in the Montgomery domain. |num_r| must be the length of the
+// modulus, which is |mont->N.top|. This function returns one on success and
+// zero on internal error or inconsistent lengths. Any two of |r|, |a|, and |b|
+// may alias.
+//
+// This function requires |a| * |b| < N * R, where N is the modulus and R is the
+// Montgomery divisor, 2^(N.top * BN_BITS2). This should generally be satisfied
+// by ensuring |a| and |b| are fully reduced, however ECDSA has one computation
+// which requires the more general bound.
+int bn_mod_mul_montgomery_small(BN_ULONG *r, size_t num_r, const BN_ULONG *a,
+ size_t num_a, const BN_ULONG *b, size_t num_b,
+ const BN_MONT_CTX *mont);
+
+// bn_mod_exp_mont_small sets |r| to |a|^|p| mod |mont->N|. It returns one on
+// success and zero on programmer or internal error. Both inputs and outputs are
+// in the Montgomery domain. |num_r| and |num_a| must be |mont->N.top|, which
+// must be at most |BN_SMALL_MAX_WORDS|. |a| must be fully-reduced. This
+// function runs in time independent of |a|, but |p| and |mont->N| are public
+// values.
+//
+// Note this function differs from |BN_mod_exp_mont| which uses Montgomery
+// reduction but takes input and output outside the Montgomery domain. Combine
+// this function with |bn_from_montgomery_small| and |bn_to_montgomery_small|
+// if necessary.
+int bn_mod_exp_mont_small(BN_ULONG *r, size_t num_r, const BN_ULONG *a,
+ size_t num_a, const BN_ULONG *p, size_t num_p,
+ const BN_MONT_CTX *mont);
+
+// bn_mod_inverse_prime_mont_small sets |r| to |a|^-1 mod |mont->N|. |mont->N|
+// must be a prime. |num_r| and |num_a| must be |mont->N.top|, which must be at
+// most |BN_SMALL_MAX_WORDS|. |a| must be fully-reduced. This function runs in
+// time independent of |a|, but |mont->N| is a public value.
+int bn_mod_inverse_prime_mont_small(BN_ULONG *r, size_t num_r,
+ const BN_ULONG *a, size_t num_a,
+ const BN_MONT_CTX *mont);
+
#if defined(__cplusplus)
} // extern C
diff --git a/src/crypto/fipsmodule/bn/montgomery.c b/src/crypto/fipsmodule/bn/montgomery.c
index caa2513..e8505da 100644
--- a/src/crypto/fipsmodule/bn/montgomery.c
+++ b/src/crypto/fipsmodule/bn/montgomery.c
@@ -114,6 +114,7 @@
#include <openssl/err.h>
#include <openssl/mem.h>
#include <openssl/thread.h>
+#include <openssl/type_check.h>
#include "internal.h"
#include "../../internal.h"
@@ -259,87 +260,75 @@
return BN_mod_mul_montgomery(ret, a, &mont->RR, mont, ctx);
}
+static int bn_from_montgomery_in_place(BN_ULONG *r, size_t num_r, BN_ULONG *a,
+ size_t num_a, const BN_MONT_CTX *mont) {
+ const BN_ULONG *n = mont->N.d;
+ size_t num_n = mont->N.top;
+ if (num_r != num_n || num_a != 2 * num_n) {
+ OPENSSL_PUT_ERROR(BN, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
+ return 0;
+ }
+
+ // Add multiples of |n| to |r| until R = 2^(nl * BN_BITS2) divides it. On
+ // input, we had |r| < |n| * R, so now |r| < 2 * |n| * R. Note that |r|
+ // includes |carry| which is stored separately.
+ BN_ULONG n0 = mont->n0[0];
+ BN_ULONG carry = 0;
+ for (size_t i = 0; i < num_n; i++) {
+ BN_ULONG v = bn_mul_add_words(a + i, n, num_n, a[i] * n0);
+ v += carry + a[i + num_n];
+ carry |= (v != a[i + num_n]);
+ carry &= (v <= a[i + num_n]);
+ a[i + num_n] = v;
+ }
+
+ // Shift |num_n| words to divide by R. We have |a| < 2 * |n|. Note that |a|
+ // includes |carry| which is stored separately.
+ a += num_n;
+
+ // |a| thus requires at most one additional subtraction |n| to be reduced.
+ // Subtract |n| and select the answer in constant time.
+ OPENSSL_COMPILE_ASSERT(sizeof(BN_ULONG) <= sizeof(crypto_word_t),
+ crypto_word_t_too_small);
+ BN_ULONG v = bn_sub_words(r, a, n, num_n) - carry;
+ // |v| is one if |a| - |n| underflowed or zero if it did not. Note |v| cannot
+ // be -1. That would imply the subtraction did not fit in |num_n| words, and
+ // we know at most one subtraction is needed.
+ v = 0u - v;
+ for (size_t i = 0; i < num_n; i++) {
+ r[i] = constant_time_select_w(v, a[i], r[i]);
+ a[i] = 0;
+ }
+ return 1;
+}
+
static int BN_from_montgomery_word(BIGNUM *ret, BIGNUM *r,
const BN_MONT_CTX *mont) {
- BN_ULONG *ap, *np, *rp, n0, v, carry;
- int nl, max, i;
-
const BIGNUM *n = &mont->N;
- nl = n->top;
- if (nl == 0) {
+ if (n->top == 0) {
ret->top = 0;
return 1;
}
- max = (2 * nl); // carry is stored separately
- if (!bn_wexpand(r, max)) {
+ int max = (2 * n->top); // carry is stored separately
+ if (!bn_wexpand(r, max) ||
+ !bn_wexpand(ret, n->top)) {
return 0;
}
-
- r->neg ^= n->neg;
- np = n->d;
- rp = r->d;
-
- // clear the top words of T
+ // Clear the top words of |r|.
if (max > r->top) {
- OPENSSL_memset(&rp[r->top], 0, (max - r->top) * sizeof(BN_ULONG));
+ OPENSSL_memset(r->d + r->top, 0, (max - r->top) * sizeof(BN_ULONG));
}
-
r->top = max;
- n0 = mont->n0[0];
+ ret->top = n->top;
- for (carry = 0, i = 0; i < nl; i++, rp++) {
- v = bn_mul_add_words(rp, np, nl, rp[0] * n0);
- v += carry + rp[nl];
- carry |= (v != rp[nl]);
- carry &= (v <= rp[nl]);
- rp[nl] = v;
- }
-
- if (!bn_wexpand(ret, nl)) {
+ if (!bn_from_montgomery_in_place(ret->d, ret->top, r->d, r->top, mont)) {
return 0;
}
- ret->top = nl;
ret->neg = r->neg;
- rp = ret->d;
- ap = &(r->d[nl]);
-
- {
- BN_ULONG *nrp;
- uintptr_t m;
-
- v = bn_sub_words(rp, ap, np, nl) - carry;
- // if subtraction result is real, then trick unconditional memcpy below to
- // perform in-place "refresh" instead of actual copy.
- m = (0u - (uintptr_t)v);
- nrp = (BN_ULONG *)(((uintptr_t)rp & ~m) | ((uintptr_t)ap & m));
-
- for (i = 0, nl -= 4; i < nl; i += 4) {
- BN_ULONG t1, t2, t3, t4;
-
- t1 = nrp[i + 0];
- t2 = nrp[i + 1];
- t3 = nrp[i + 2];
- ap[i + 0] = 0;
- t4 = nrp[i + 3];
- ap[i + 1] = 0;
- rp[i + 0] = t1;
- ap[i + 2] = 0;
- rp[i + 1] = t2;
- ap[i + 3] = 0;
- rp[i + 2] = t3;
- rp[i + 3] = t4;
- }
-
- for (nl += 4; i < nl; i++) {
- rp[i] = nrp[i], ap[i] = 0;
- }
- }
-
bn_correct_top(r);
bn_correct_top(ret);
-
return 1;
}
@@ -427,3 +416,68 @@
BN_CTX_end(ctx);
return ret;
}
+
+int bn_to_montgomery_small(BN_ULONG *r, size_t num_r, const BN_ULONG *a,
+ size_t num_a, const BN_MONT_CTX *mont) {
+ return bn_mod_mul_montgomery_small(r, num_r, a, num_a, mont->RR.d,
+ mont->RR.top, mont);
+}
+
+int bn_from_montgomery_small(BN_ULONG *r, size_t num_r, const BN_ULONG *a,
+ size_t num_a, const BN_MONT_CTX *mont) {
+ size_t num_n = mont->N.top;
+ if (num_a > 2 * num_n || num_r != num_n || num_n > BN_SMALL_MAX_WORDS) {
+ OPENSSL_PUT_ERROR(BN, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
+ return 0;
+ }
+ BN_ULONG tmp[BN_SMALL_MAX_WORDS * 2];
+ size_t num_tmp = 2 * num_n;
+ OPENSSL_memcpy(tmp, a, num_a * sizeof(BN_ULONG));
+ OPENSSL_memset(tmp + num_a, 0, (num_tmp - num_a) * sizeof(BN_ULONG));
+ int ret = bn_from_montgomery_in_place(r, num_r, tmp, num_tmp, mont);
+ OPENSSL_cleanse(tmp, num_tmp * sizeof(BN_ULONG));
+ return ret;
+}
+
+int bn_mod_mul_montgomery_small(BN_ULONG *r, size_t num_r, const BN_ULONG *a,
+ size_t num_a, const BN_ULONG *b, size_t num_b,
+ const BN_MONT_CTX *mont) {
+ size_t num_n = mont->N.top;
+ if (num_r != num_n || num_a + num_b > 2 * num_n ||
+ num_n > BN_SMALL_MAX_WORDS) {
+ OPENSSL_PUT_ERROR(BN, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
+ return 0;
+ }
+
+#if defined(OPENSSL_BN_ASM_MONT)
+ // |bn_mul_mont| requires at least 128 bits of limbs, at least for x86.
+ if (num_n >= (128 / BN_BITS2) &&
+ num_a == num_n &&
+ num_b == num_n) {
+ if (!bn_mul_mont(r, a, b, mont->N.d, mont->n0, num_n)) {
+ assert(0); // The check above ensures this won't happen.
+ OPENSSL_PUT_ERROR(BN, ERR_R_INTERNAL_ERROR);
+ return 0;
+ }
+ return 1;
+ }
+#endif
+
+ // Compute the product.
+ BN_ULONG tmp[2 * BN_SMALL_MAX_WORDS];
+ size_t num_tmp = 2 * num_n;
+ size_t num_ab = num_a + num_b;
+ if (a == b && num_a == num_b) {
+ if (!bn_sqr_small(tmp, num_ab, a, num_a)) {
+ return 0;
+ }
+ } else if (!bn_mul_small(tmp, num_ab, a, num_a, b, num_b)) {
+ return 0;
+ }
+
+ // Zero-extend to full width and reduce.
+ OPENSSL_memset(tmp + num_ab, 0, (num_tmp - num_ab) * sizeof(BN_ULONG));
+ int ret = bn_from_montgomery_in_place(r, num_r, tmp, num_tmp, mont);
+ OPENSSL_cleanse(tmp, num_tmp * sizeof(BN_ULONG));
+ return ret;
+}
diff --git a/src/crypto/fipsmodule/bn/mul.c b/src/crypto/fipsmodule/bn/mul.c
index 65f3c2b..b93f558 100644
--- a/src/crypto/fipsmodule/bn/mul.c
+++ b/src/crypto/fipsmodule/bn/mul.c
@@ -59,50 +59,48 @@
#include <assert.h>
#include <string.h>
+#include <openssl/err.h>
+#include <openssl/mem.h>
+
#include "internal.h"
+#include "../../internal.h"
#define BN_MUL_RECURSIVE_SIZE_NORMAL 16
#define BN_SQR_RECURSIVE_SIZE_NORMAL BN_MUL_RECURSIVE_SIZE_NORMAL
-static void bn_mul_normal(BN_ULONG *r, BN_ULONG *a, int na, BN_ULONG *b,
- int nb) {
- BN_ULONG *rr;
-
+static void bn_mul_normal(BN_ULONG *r, const BN_ULONG *a, size_t na,
+ const BN_ULONG *b, size_t nb) {
if (na < nb) {
- int itmp;
- BN_ULONG *ltmp;
-
- itmp = na;
+ size_t itmp = na;
na = nb;
nb = itmp;
- ltmp = a;
+ const BN_ULONG *ltmp = a;
a = b;
b = ltmp;
}
- rr = &(r[na]);
- if (nb <= 0) {
- (void)bn_mul_words(r, a, na, 0);
+ BN_ULONG *rr = &(r[na]);
+ if (nb == 0) {
+ OPENSSL_memset(r, 0, na * sizeof(BN_ULONG));
return;
- } else {
- rr[0] = bn_mul_words(r, a, na, b[0]);
}
+ rr[0] = bn_mul_words(r, a, na, b[0]);
for (;;) {
- if (--nb <= 0) {
+ if (--nb == 0) {
return;
}
rr[1] = bn_mul_add_words(&(r[1]), a, na, b[1]);
- if (--nb <= 0) {
+ if (--nb == 0) {
return;
}
rr[2] = bn_mul_add_words(&(r[2]), a, na, b[2]);
- if (--nb <= 0) {
+ if (--nb == 0) {
return;
}
rr[3] = bn_mul_add_words(&(r[3]), a, na, b[3]);
- if (--nb <= 0) {
+ if (--nb == 0) {
return;
}
rr[4] = bn_mul_add_words(&(r[4]), a, na, b[4]);
@@ -294,8 +292,8 @@
// a[0]*b[0]+a[1]*b[1]+(a[0]-a[1])*(b[1]-b[0])
// a[1]*b[1]
// dnX may not be positive, but n2/2+dnX has to be
-static void bn_mul_recursive(BN_ULONG *r, BN_ULONG *a, BN_ULONG *b, int n2,
- int dna, int dnb, BN_ULONG *t) {
+static void bn_mul_recursive(BN_ULONG *r, const BN_ULONG *a, const BN_ULONG *b,
+ int n2, int dna, int dnb, BN_ULONG *t) {
int n = n2 / 2, c1, c2;
int tna = n + dna, tnb = n + dnb;
unsigned int neg, zero;
@@ -426,8 +424,9 @@
// n+tn is the word length
// t needs to be n*4 is size, as does r
// tnX may not be negative but less than n
-static void bn_mul_part_recursive(BN_ULONG *r, BN_ULONG *a, BN_ULONG *b, int n,
- int tna, int tnb, BN_ULONG *t) {
+static void bn_mul_part_recursive(BN_ULONG *r, const BN_ULONG *a,
+ const BN_ULONG *b, int n, int tna, int tnb,
+ BN_ULONG *t) {
int i, j, n2 = n * 2;
int c1, c2, neg;
BN_ULONG ln, lo, *p;
@@ -658,28 +657,47 @@
return ret;
}
+int bn_mul_small(BN_ULONG *r, size_t num_r, const BN_ULONG *a, size_t num_a,
+ const BN_ULONG *b, size_t num_b) {
+ if (num_r != num_a + num_b) {
+ OPENSSL_PUT_ERROR(BN, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
+ return 0;
+ }
+ // TODO(davidben): Should this call |bn_mul_comba4| too? |BN_mul| does not
+ // hit that code.
+ if (num_a == 8 && num_b == 8) {
+ bn_mul_comba8(r, a, b);
+ } else {
+ bn_mul_normal(r, a, num_a, b, num_b);
+ }
+ return 1;
+}
+
// tmp must have 2*n words
-static void bn_sqr_normal(BN_ULONG *r, const BN_ULONG *a, int n,
+static void bn_sqr_normal(BN_ULONG *r, const BN_ULONG *a, size_t n,
BN_ULONG *tmp) {
- int max = n * 2;
+ if (n == 0) {
+ return;
+ }
+
+ size_t max = n * 2;
const BN_ULONG *ap = a;
BN_ULONG *rp = r;
rp[0] = rp[max - 1] = 0;
rp++;
- int j = n;
// Compute the contribution of a[i] * a[j] for all i < j.
- if (--j > 0) {
+ if (n > 1) {
ap++;
- rp[j] = bn_mul_words(rp, ap, j, ap[-1]);
+ rp[n - 1] = bn_mul_words(rp, ap, n - 1, ap[-1]);
rp += 2;
}
-
- for (int i = n - 2; i > 0; i--) {
- j--;
- ap++;
- rp[j] = bn_mul_add_words(rp, ap, j, ap[-1]);
- rp += 2;
+ if (n > 2) {
+ for (size_t i = n - 2; i > 0; i--) {
+ ap++;
+ rp[i] = bn_mul_add_words(rp, ap, i, ap[-1]);
+ rp += 2;
+ }
}
// The final result fits in |max| words, so none of the following operations
@@ -865,3 +883,20 @@
BN_CTX_end(ctx);
return ret;
}
+
+int bn_sqr_small(BN_ULONG *r, size_t num_r, const BN_ULONG *a, size_t num_a) {
+ if (num_r != 2 * num_a || num_a > BN_SMALL_MAX_WORDS) {
+ OPENSSL_PUT_ERROR(BN, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
+ return 0;
+ }
+ if (num_a == 4) {
+ bn_sqr_comba4(r, a);
+ } else if (num_a == 8) {
+ bn_sqr_comba8(r, a);
+ } else {
+ BN_ULONG tmp[2 * BN_SMALL_MAX_WORDS];
+ bn_sqr_normal(r, a, num_a, tmp);
+ OPENSSL_cleanse(tmp, 2 * num_a * sizeof(BN_ULONG));
+ }
+ return 1;
+}
diff --git a/src/crypto/fipsmodule/bn/random.c b/src/crypto/fipsmodule/bn/random.c
index 2257da0..61499af 100644
--- a/src/crypto/fipsmodule/bn/random.c
+++ b/src/crypto/fipsmodule/bn/random.c
@@ -113,18 +113,16 @@
#include <openssl/err.h>
#include <openssl/mem.h>
#include <openssl/rand.h>
-#include <openssl/sha.h>
#include <openssl/type_check.h>
+#include "internal.h"
#include "../../internal.h"
#include "../rand/internal.h"
static const uint8_t kDefaultAdditionalData[32] = {0};
-static int bn_rand_with_additional_data(BIGNUM *rnd, int bits, int top,
- int bottom,
- const uint8_t additional_data[32]) {
+int BN_rand(BIGNUM *rnd, int bits, int top, int bottom) {
uint8_t *buf = NULL;
int ret = 0, bit, bytes, mask;
@@ -159,7 +157,7 @@
}
// Make a random number and set the top and bottom bits.
- RAND_bytes_with_additional_data(buf, bytes, additional_data);
+ RAND_bytes(buf, bytes);
if (top != BN_RAND_TOP_ANY) {
if (top == BN_RAND_TOP_TWO && bits > 1) {
@@ -192,54 +190,104 @@
return ret;
}
-int BN_rand(BIGNUM *rnd, int bits, int top, int bottom) {
- return bn_rand_with_additional_data(rnd, bits, top, bottom,
- kDefaultAdditionalData);
-}
-
int BN_pseudo_rand(BIGNUM *rnd, int bits, int top, int bottom) {
return BN_rand(rnd, bits, top, bottom);
}
-static int bn_rand_range_with_additional_data(
- BIGNUM *r, BN_ULONG min_inclusive, const BIGNUM *max_exclusive,
- const uint8_t additional_data[32]) {
- if (BN_cmp_word(max_exclusive, min_inclusive) <= 0) {
+// bn_less_than_word returns one if the number represented by |len| words at |a|
+// is less than |b| and zero otherwise. It performs this computation in time
+// independent of the value of |a|. |b| is assumed public.
+static int bn_less_than_word(const BN_ULONG *a, size_t len, BN_ULONG b) {
+ if (b == 0) {
+ return 0;
+ }
+ if (len == 0) {
+ return 1;
+ }
+
+ // |a| < |b| iff a[1..len-1] are all zero and a[0] < b.
+ OPENSSL_COMPILE_ASSERT(sizeof(BN_ULONG) <= sizeof(crypto_word_t),
+ crypto_word_t_too_small);
+ crypto_word_t mask = 0;
+ for (size_t i = 1; i < len; i++) {
+ mask |= a[i];
+ }
+ // |mask| is now zero iff a[1..len-1] are all zero.
+ mask = constant_time_is_zero_w(mask);
+ mask &= constant_time_lt_w(a[0], b);
+ return constant_time_select_int(mask, 1, 0);
+}
+
+int bn_in_range_words(const BN_ULONG *a, BN_ULONG min_inclusive,
+ const BN_ULONG *max_exclusive, size_t len) {
+ return bn_less_than_words(a, max_exclusive, len) &&
+ !bn_less_than_word(a, len, min_inclusive);
+}
+
+int bn_rand_range_words(BN_ULONG *out, BN_ULONG min_inclusive,
+ const BN_ULONG *max_exclusive, size_t len,
+ const uint8_t additional_data[32]) {
+ // This function implements the equivalent of steps 4 through 7 of FIPS 186-4
+ // appendices B.4.2 and B.5.2. When called in those contexts, |max_exclusive|
+ // is n and |min_inclusive| is one.
+
+ // Compute the bit length of |max_exclusive| (step 1), in terms of a number of
+ // |words| worth of entropy to fill and a mask of bits to clear in the top
+ // word.
+ size_t words = len;
+ while (words > 0 && max_exclusive[words - 1] == 0) {
+ words--;
+ }
+ if (words == 0 ||
+ (words == 1 && max_exclusive[0] <= min_inclusive)) {
OPENSSL_PUT_ERROR(BN, BN_R_INVALID_RANGE);
return 0;
}
+ BN_ULONG mask = max_exclusive[words - 1];
+ // This sets all bits in |mask| below the most significant bit.
+ mask |= mask >> 1;
+ mask |= mask >> 2;
+ mask |= mask >> 4;
+ mask |= mask >> 8;
+ mask |= mask >> 16;
+#if defined(OPENSSL_64_BIT)
+ mask |= mask >> 32;
+#endif
- // This function is used to implement steps 4 through 7 of FIPS 186-4
- // appendices B.4.2 and B.5.2. When called in those contexts, |max_exclusive|
- // is n and |min_inclusive| is one.
+ // Fill any unused words with zero.
+ OPENSSL_memset(out + words, 0, (len - words) * sizeof(BN_ULONG));
+
unsigned count = 100;
- unsigned n = BN_num_bits(max_exclusive); // n > 0
do {
if (!--count) {
OPENSSL_PUT_ERROR(BN, BN_R_TOO_MANY_ITERATIONS);
return 0;
}
- if (// steps 4 and 5
- !bn_rand_with_additional_data(r, n, BN_RAND_TOP_ANY, BN_RAND_BOTTOM_ANY,
- additional_data) ||
- // step 7
- !BN_add_word(r, min_inclusive)) {
- return 0;
- }
+ // Steps 4 and 5. Use |words| and |mask| together to obtain a string of N
+ // bits, where N is the bit length of |max_exclusive|.
+ RAND_bytes_with_additional_data((uint8_t *)out, words * sizeof(BN_ULONG),
+ additional_data);
+ out[words - 1] &= mask;
- // Step 6. This loops if |r| >= |max_exclusive|. This is identical to
- // checking |r| > |max_exclusive| - 1 or |r| - 1 > |max_exclusive| - 2, the
- // formulation stated in FIPS 186-4.
- } while (BN_cmp(r, max_exclusive) >= 0);
-
+ // If out >= max_exclusive or out < min_inclusive, retry. This implements
+ // the equivalent of steps 6 and 7 without leaking the value of |out|.
+ } while (!bn_in_range_words(out, min_inclusive, max_exclusive, words));
return 1;
}
int BN_rand_range_ex(BIGNUM *r, BN_ULONG min_inclusive,
const BIGNUM *max_exclusive) {
- return bn_rand_range_with_additional_data(r, min_inclusive, max_exclusive,
- kDefaultAdditionalData);
+ if (!bn_wexpand(r, max_exclusive->top) ||
+ !bn_rand_range_words(r->d, min_inclusive, max_exclusive->d,
+ max_exclusive->top, kDefaultAdditionalData)) {
+ return 0;
+ }
+
+ r->neg = 0;
+ r->top = max_exclusive->top;
+ bn_correct_top(r);
+ return 1;
}
int BN_rand_range(BIGNUM *r, const BIGNUM *range) {
@@ -249,35 +297,3 @@
int BN_pseudo_rand_range(BIGNUM *r, const BIGNUM *range) {
return BN_rand_range(r, range);
}
-
-int BN_generate_dsa_nonce(BIGNUM *out, const BIGNUM *range, const BIGNUM *priv,
- const uint8_t *message, size_t message_len,
- BN_CTX *ctx) {
- // We copy |priv| into a local buffer to avoid furthur exposing its
- // length.
- uint8_t private_bytes[96];
- size_t todo = sizeof(priv->d[0]) * priv->top;
- if (todo > sizeof(private_bytes)) {
- // No reasonable DSA or ECDSA key should have a private key
- // this large and we don't handle this case in order to avoid
- // leaking the length of the private key.
- OPENSSL_PUT_ERROR(BN, BN_R_PRIVATE_KEY_TOO_LARGE);
- return 0;
- }
- OPENSSL_memcpy(private_bytes, priv->d, todo);
- OPENSSL_memset(private_bytes + todo, 0, sizeof(private_bytes) - todo);
-
- // Pass a SHA512 hash of the private key and message as additional data into
- // the RBG. This is a hardening measure against entropy failure.
- OPENSSL_COMPILE_ASSERT(SHA512_DIGEST_LENGTH >= 32,
- additional_data_is_too_large_for_sha512);
- SHA512_CTX sha;
- uint8_t digest[SHA512_DIGEST_LENGTH];
- SHA512_Init(&sha);
- SHA512_Update(&sha, private_bytes, sizeof(private_bytes));
- SHA512_Update(&sha, message, message_len);
- SHA512_Final(digest, &sha);
-
- // Select a value k from [1, range-1], following FIPS 186-4 appendix B.5.2.
- return bn_rand_range_with_additional_data(out, 1, range, digest);
-}
diff --git a/src/crypto/fipsmodule/bn/shift.c b/src/crypto/fipsmodule/bn/shift.c
index d4528e6..d4ed79e 100644
--- a/src/crypto/fipsmodule/bn/shift.c
+++ b/src/crypto/fipsmodule/bn/shift.c
@@ -267,17 +267,20 @@
return 1;
}
+int bn_is_bit_set_words(const BN_ULONG *a, size_t num, unsigned bit) {
+ unsigned i = bit / BN_BITS2;
+ unsigned j = bit % BN_BITS2;
+ if (i >= num) {
+ return 0;
+ }
+ return (a[i] >> j) & 1;
+}
+
int BN_is_bit_set(const BIGNUM *a, int n) {
if (n < 0) {
return 0;
}
- int i = n / BN_BITS2;
- int j = n % BN_BITS2;
- if (a->top <= i) {
- return 0;
- }
-
- return (a->d[i]>>j)&1;
+ return bn_is_bit_set_words(a->d, a->top, n);
}
int BN_mask_bits(BIGNUM *a, int n) {
diff --git a/src/crypto/fipsmodule/ec/ec.c b/src/crypto/fipsmodule/ec/ec.c
index 600aedc..977cd26 100644
--- a/src/crypto/fipsmodule/ec/ec.c
+++ b/src/crypto/fipsmodule/ec/ec.c
@@ -77,6 +77,7 @@
#include "internal.h"
#include "../../internal.h"
+#include "../bn/internal.h"
#include "../delocate.h"
@@ -279,63 +280,6 @@
#endif
}
-// built_in_curve_scalar_field_monts contains Montgomery contexts for
-// performing inversions in the scalar fields of each of the built-in
-// curves. It's protected by |built_in_curve_scalar_field_monts_once|.
-DEFINE_LOCAL_DATA(BN_MONT_CTX **, built_in_curve_scalar_field_monts) {
- const struct built_in_curves *const curves = OPENSSL_built_in_curves();
-
- BN_MONT_CTX **monts =
- OPENSSL_malloc(sizeof(BN_MONT_CTX *) * OPENSSL_NUM_BUILT_IN_CURVES);
- if (monts == NULL) {
- return;
- }
-
- OPENSSL_memset(monts, 0, sizeof(BN_MONT_CTX *) * OPENSSL_NUM_BUILT_IN_CURVES);
-
- BIGNUM *order = BN_new();
- BN_CTX *bn_ctx = BN_CTX_new();
- BN_MONT_CTX *mont_ctx = NULL;
-
- if (bn_ctx == NULL ||
- order == NULL) {
- goto err;
- }
-
- for (size_t i = 0; i < OPENSSL_NUM_BUILT_IN_CURVES; i++) {
- const struct built_in_curve *curve = &curves->curves[i];
- const unsigned param_len = curve->param_len;
- const uint8_t *params = curve->params;
-
- mont_ctx = BN_MONT_CTX_new();
- if (mont_ctx == NULL) {
- goto err;
- }
-
- if (!BN_bin2bn(params + 5 * param_len, param_len, order) ||
- !BN_MONT_CTX_set(mont_ctx, order, bn_ctx)) {
- goto err;
- }
-
- monts[i] = mont_ctx;
- mont_ctx = NULL;
- }
-
- *out = monts;
- goto done;
-
-err:
- BN_MONT_CTX_free(mont_ctx);
- for (size_t i = 0; i < OPENSSL_NUM_BUILT_IN_CURVES; i++) {
- BN_MONT_CTX_free(monts[i]);
- }
- OPENSSL_free((BN_MONT_CTX**) monts);
-
-done:
- BN_free(order);
- BN_CTX_free(bn_ctx);
-}
-
EC_GROUP *ec_group_new(const EC_METHOD *meth) {
EC_GROUP *ret;
@@ -383,6 +327,11 @@
EC_GROUP *EC_GROUP_new_curve_GFp(const BIGNUM *p, const BIGNUM *a,
const BIGNUM *b, BN_CTX *ctx) {
+ if (BN_num_bytes(p) > EC_MAX_SCALAR_BYTES) {
+ OPENSSL_PUT_ERROR(EC, EC_R_INVALID_FIELD);
+ return NULL;
+ }
+
EC_GROUP *ret = ec_group_new(EC_GFp_mont_method());
if (ret == NULL) {
return NULL;
@@ -409,6 +358,12 @@
// Additionally, |generator| must been created from
// |EC_GROUP_new_curve_GFp|, not a copy, so that
// |generator->group->generator| is set correctly.
+ OPENSSL_PUT_ERROR(EC, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
+ return 0;
+ }
+
+ if (BN_num_bytes(order) > EC_MAX_SCALAR_BYTES) {
+ OPENSSL_PUT_ERROR(EC, EC_R_INVALID_FIELD);
return 0;
}
@@ -418,6 +373,24 @@
return 0;
}
+ // Require that p < 2×order. This simplifies some ECDSA operations.
+ //
+ // Note any curve which did not satisfy this must have been invalid or use a
+ // tiny prime (less than 17). See the proof in |field_element_to_scalar| in
+ // the ECDSA implementation.
+ BIGNUM *tmp = BN_new();
+ if (tmp == NULL ||
+ !BN_lshift1(tmp, order)) {
+ BN_free(tmp);
+ return 0;
+ }
+ int ok = BN_cmp(tmp, &group->field) > 0;
+ BN_free(tmp);
+ if (!ok) {
+ OPENSSL_PUT_ERROR(EC, EC_R_INVALID_GROUP_ORDER);
+ return 0;
+ }
+
EC_POINT *copy = EC_POINT_new(group);
if (copy == NULL ||
!EC_POINT_copy(copy, generator) ||
@@ -426,13 +399,18 @@
return 0;
}
+ BN_MONT_CTX_free(group->order_mont);
+ group->order_mont = BN_MONT_CTX_new();
+ if (group->order_mont == NULL ||
+ !BN_MONT_CTX_set(group->order_mont, &group->order, NULL)) {
+ return 0;
+ }
+
ec_group_set0_generator(group, copy);
return 1;
}
-static EC_GROUP *ec_group_new_from_data(unsigned built_in_index) {
- const struct built_in_curves *const curves = OPENSSL_built_in_curves();
- const struct built_in_curve *curve = &curves->curves[built_in_index];
+static EC_GROUP *ec_group_new_from_data(const struct built_in_curve *curve) {
EC_GROUP *group = NULL;
EC_POINT *P = NULL;
BIGNUM *p = NULL, *a = NULL, *b = NULL, *x = NULL, *y = NULL;
@@ -481,9 +459,11 @@
goto err;
}
- const BN_MONT_CTX **monts = *built_in_curve_scalar_field_monts();
- if (monts != NULL) {
- group->order_mont = monts[built_in_index];
+ group->order_mont = BN_MONT_CTX_new();
+ if (group->order_mont == NULL ||
+ !BN_MONT_CTX_set(group->order_mont, &group->order, ctx)) {
+ OPENSSL_PUT_ERROR(EC, ERR_R_BN_LIB);
+ goto err;
}
ec_group_set0_generator(group, P);
@@ -505,29 +485,65 @@
return group;
}
-EC_GROUP *EC_GROUP_new_by_curve_name(int nid) {
- const struct built_in_curves *const curves = OPENSSL_built_in_curves();
- EC_GROUP *ret = NULL;
+// Built-in groups are allocated lazily and static once allocated.
+// TODO(davidben): Make these actually static. https://crbug.com/boringssl/20.
+struct built_in_groups_st {
+ EC_GROUP *groups[OPENSSL_NUM_BUILT_IN_CURVES];
+};
+DEFINE_BSS_GET(struct built_in_groups_st, built_in_groups);
+DEFINE_STATIC_MUTEX(built_in_groups_lock);
+EC_GROUP *EC_GROUP_new_by_curve_name(int nid) {
+ struct built_in_groups_st *groups = built_in_groups_bss_get();
+ EC_GROUP **group_ptr = NULL;
+ const struct built_in_curves *const curves = OPENSSL_built_in_curves();
+ const struct built_in_curve *curve = NULL;
for (size_t i = 0; i < OPENSSL_NUM_BUILT_IN_CURVES; i++) {
- const struct built_in_curve *curve = &curves->curves[i];
- if (curve->nid == nid) {
- ret = ec_group_new_from_data(i);
+ if (curves->curves[i].nid == nid) {
+ curve = &curves->curves[i];
+ group_ptr = &groups->groups[i];
break;
}
}
- if (ret == NULL) {
+ if (curve == NULL) {
OPENSSL_PUT_ERROR(EC, EC_R_UNKNOWN_GROUP);
return NULL;
}
- ret->curve_name = nid;
+ CRYPTO_STATIC_MUTEX_lock_read(built_in_groups_lock_bss_get());
+ EC_GROUP *ret = *group_ptr;
+ CRYPTO_STATIC_MUTEX_unlock_read(built_in_groups_lock_bss_get());
+ if (ret != NULL) {
+ return ret;
+ }
+
+ ret = ec_group_new_from_data(curve);
+ if (ret == NULL) {
+ return NULL;
+ }
+
+ EC_GROUP *to_free = NULL;
+ CRYPTO_STATIC_MUTEX_lock_write(built_in_groups_lock_bss_get());
+ if (*group_ptr == NULL) {
+ *group_ptr = ret;
+ // Filling in |ret->curve_name| makes |EC_GROUP_free| and |EC_GROUP_dup|
+ // into no-ops. At this point, |ret| is considered static.
+ ret->curve_name = nid;
+ } else {
+ to_free = ret;
+ ret = *group_ptr;
+ }
+ CRYPTO_STATIC_MUTEX_unlock_write(built_in_groups_lock_bss_get());
+
+ EC_GROUP_free(to_free);
return ret;
}
void EC_GROUP_free(EC_GROUP *group) {
if (group == NULL ||
+ // Built-in curves are static.
+ group->curve_name != NID_undef ||
!CRYPTO_refcount_dec_and_test_zero(&group->references)) {
return;
}
@@ -538,17 +554,16 @@
ec_point_free(group->generator, 0 /* don't free group */);
BN_free(&group->order);
+ BN_MONT_CTX_free(group->order_mont);
OPENSSL_free(group);
}
-const BN_MONT_CTX *ec_group_get_order_mont(const EC_GROUP *group) {
- return group->order_mont;
-}
-
EC_GROUP *EC_GROUP_dup(const EC_GROUP *a) {
- if (a == NULL) {
- return NULL;
+ if (a == NULL ||
+ // Built-in curves are static.
+ a->curve_name != NID_undef) {
+ return (EC_GROUP *)a;
}
// Groups are logically immutable (but for |EC_GROUP_set_generator| which must
@@ -808,7 +823,53 @@
// nothing to multiply. But, nobody should be calling this function with
// nothing to multiply in the first place.
if ((g_scalar == NULL && p_scalar == NULL) ||
- ((p == NULL) != (p_scalar == NULL))) {
+ (p == NULL) != (p_scalar == NULL)) {
+ OPENSSL_PUT_ERROR(EC, ERR_R_PASSED_NULL_PARAMETER);
+ 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);
+ 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);
+ 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);
+ goto err;
+ }
+ p_scalar_arg = &p_scalar_storage;
+ }
+
+ ret = ec_point_mul_scalar(group, r, g_scalar_arg, p, p_scalar_arg, ctx);
+
+err:
+ OPENSSL_cleanse(&g_scalar_storage, sizeof(g_scalar_storage));
+ OPENSSL_cleanse(&p_scalar_storage, sizeof(p_scalar_storage));
+ return ret;
+}
+
+int ec_point_mul_scalar(const EC_GROUP *group, EC_POINT *r,
+ const EC_SCALAR *g_scalar, const EC_POINT *p,
+ const EC_SCALAR *p_scalar, BN_CTX *ctx) {
+ if ((g_scalar == NULL && p_scalar == NULL) ||
+ (p == NULL) != (p_scalar == NULL)) {
OPENSSL_PUT_ERROR(EC, ERR_R_PASSED_NULL_PARAMETER);
return 0;
}
@@ -863,3 +924,20 @@
return OPENSSL_NUM_BUILT_IN_CURVES;
}
+
+int ec_bignum_to_scalar(const EC_GROUP *group, EC_SCALAR *out,
+ const BIGNUM *in) {
+ if (BN_is_negative(in) || in->top > group->order.top) {
+ OPENSSL_PUT_ERROR(EC, EC_R_INVALID_SCALAR);
+ return 0;
+ }
+ OPENSSL_memset(out->words, 0, group->order.top * sizeof(BN_ULONG));
+ OPENSSL_memcpy(out->words, in->d, in->top * sizeof(BN_ULONG));
+ return 1;
+}
+
+int ec_random_nonzero_scalar(const EC_GROUP *group, EC_SCALAR *out,
+ const uint8_t additional_data[32]) {
+ return bn_rand_range_words(out->words, 1, group->order.d, group->order.top,
+ additional_data);
+}
diff --git a/src/crypto/fipsmodule/ec/ec_test.cc b/src/crypto/fipsmodule/ec/ec_test.cc
index 0ee7378..5e5ce94 100644
--- a/src/crypto/fipsmodule/ec/ec_test.cc
+++ b/src/crypto/fipsmodule/ec/ec_test.cc
@@ -407,6 +407,38 @@
<< "p * 0 did not return point at infinity.";
}
+// Test that multiplying by the order produces ∞ and, moreover, that callers may
+// do so. |EC_POINT_mul| is almost exclusively used with reduced scalars, with
+// this exception. This comes from consumers following NIST SP 800-56A section
+// 5.6.2.3.2. (Though all our curves have cofactor one, so this check isn't
+// useful.)
+TEST_P(ECCurveTest, MulOrder) {
+ bssl::UniquePtr<EC_GROUP> group(EC_GROUP_new_by_curve_name(GetParam().nid));
+ ASSERT_TRUE(group);
+
+ // Test that g × order = ∞.
+ bssl::UniquePtr<EC_POINT> point(EC_POINT_new(group.get()));
+ ASSERT_TRUE(point);
+ ASSERT_TRUE(EC_POINT_mul(group.get(), point.get(),
+ EC_GROUP_get0_order(group.get()), nullptr, nullptr,
+ nullptr));
+
+ EXPECT_TRUE(EC_POINT_is_at_infinity(group.get(), point.get()))
+ << "g * order did not return point at infinity.";
+
+ // Test that p × order = ∞, for some arbitrary p.
+ bssl::UniquePtr<BIGNUM> forty_two(BN_new());
+ ASSERT_TRUE(forty_two);
+ ASSERT_TRUE(BN_set_word(forty_two.get(), 42));
+ ASSERT_TRUE(EC_POINT_mul(group.get(), point.get(), forty_two.get(), nullptr,
+ nullptr, nullptr));
+ ASSERT_TRUE(EC_POINT_mul(group.get(), point.get(), nullptr, point.get(),
+ EC_GROUP_get0_order(group.get()), nullptr));
+
+ EXPECT_TRUE(EC_POINT_is_at_infinity(group.get(), point.get()))
+ << "p * order did not return point at infinity.";
+}
+
// 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/fipsmodule/ec/internal.h b/src/crypto/fipsmodule/ec/internal.h
index f065744..7374e8b 100644
--- a/src/crypto/fipsmodule/ec/internal.h
+++ b/src/crypto/fipsmodule/ec/internal.h
@@ -73,12 +73,34 @@
#include <openssl/bn.h>
#include <openssl/ex_data.h>
#include <openssl/thread.h>
+#include <openssl/type_check.h>
+
+#include "../bn/internal.h"
#if defined(__cplusplus)
extern "C" {
#endif
+// Cap the size of all field elements and scalars, including custom curves, to
+// 66 bytes, large enough to fit secp521r1 and brainpoolP512r1, which appear to
+// be the largest fields anyone plausibly uses.
+#define EC_MAX_SCALAR_BYTES 66
+#define EC_MAX_SCALAR_WORDS ((66 + BN_BYTES - 1) / BN_BYTES)
+
+OPENSSL_COMPILE_ASSERT(EC_MAX_SCALAR_WORDS <= BN_SMALL_MAX_WORDS,
+ bn_small_functions_applicable);
+
+// An EC_SCALAR is a |BN_num_bits(order)|-bit integer. Only the first
+// |order->top| words are used. An |EC_SCALAR| is specific to an |EC_GROUP| and
+// must not be mixed between groups. Unless otherwise specified, it is fully
+// reduced modulo the |order|.
+typedef union {
+ // bytes is the representation of the scalar in little-endian order.
+ uint8_t bytes[EC_MAX_SCALAR_BYTES];
+ BN_ULONG words[EC_MAX_SCALAR_WORDS];
+} EC_SCALAR;
+
struct ec_method_st {
int (*group_init)(EC_GROUP *);
void (*group_finish)(EC_GROUP *);
@@ -92,8 +114,8 @@
// Computes |r = p_scalar*p| if g_scalar is null. At least one of |g_scalar|
// and |p_scalar| must be non-null, and |p| must be non-null if |p_scalar| is
// non-null.
- int (*mul)(const EC_GROUP *group, EC_POINT *r, const BIGNUM *g_scalar,
- const EC_POINT *p, const BIGNUM *p_scalar, BN_CTX *ctx);
+ int (*mul)(const EC_GROUP *group, EC_POINT *r, const EC_SCALAR *g_scalar,
+ const EC_POINT *p, const EC_SCALAR *p_scalar, BN_CTX *ctx);
// 'field_mul' and 'field_sqr' can be used by 'add' and 'dbl' so that the
// same implementations of point operations can be used with different
@@ -120,7 +142,7 @@
int curve_name; // optional NID for named curve
- const BN_MONT_CTX *order_mont; // data for ECDSA inverse
+ BN_MONT_CTX *order_mont; // data for ECDSA inverse
// The following members are handled by the method functions,
// even if they appear generic
@@ -151,13 +173,28 @@
EC_GROUP *ec_group_new(const EC_METHOD *meth);
-// ec_group_get_order_mont returns a Montgomery context for operations modulo
-// |group|'s order. It may return NULL in the case that |group| is not a
-// built-in group.
-const BN_MONT_CTX *ec_group_get_order_mont(const EC_GROUP *group);
+// ec_bignum_to_scalar converts |in| to an |EC_SCALAR| and writes it to |*out|.
+// |in| must be non-negative and have at most |BN_num_bits(&group->order)| bits.
+// It returns one on success and zero on error. It does not ensure |in| is fully
+// reduced.
+int ec_bignum_to_scalar(const EC_GROUP *group, EC_SCALAR *out,
+ const BIGNUM *in);
-int ec_wNAF_mul(const EC_GROUP *group, EC_POINT *r, const BIGNUM *g_scalar,
- const EC_POINT *p, const BIGNUM *p_scalar, BN_CTX *ctx);
+// ec_random_nonzero_scalar sets |out| to a uniformly selected random value from
+// 1 to |group->order| - 1. It returns one on success and zero on error.
+int ec_random_nonzero_scalar(const EC_GROUP *group, EC_SCALAR *out,
+ const uint8_t additional_data[32]);
+
+// ec_point_mul_scalar sets |r| to generator * |g_scalar| + |p| *
+// |p_scalar|. Unlike other functions which take |EC_SCALAR|, |g_scalar| and
+// |p_scalar| need not be fully reduced. They need only contain as many bits as
+// the order.
+int ec_point_mul_scalar(const EC_GROUP *group, EC_POINT *r,
+ const EC_SCALAR *g_scalar, const EC_POINT *p,
+ const EC_SCALAR *p_scalar, BN_CTX *ctx);
+
+int ec_wNAF_mul(const EC_GROUP *group, EC_POINT *r, const EC_SCALAR *g_scalar,
+ const EC_POINT *p, const EC_SCALAR *p_scalar, BN_CTX *ctx);
// method functions in simple.c
int ec_GFp_simple_group_init(EC_GROUP *);
@@ -175,10 +212,6 @@
const BIGNUM *x,
const BIGNUM *y,
const BIGNUM *z, BN_CTX *);
-int ec_GFp_simple_get_Jprojective_coordinates_GFp(const EC_GROUP *,
- const EC_POINT *, BIGNUM *x,
- BIGNUM *y, BIGNUM *z,
- BN_CTX *);
int ec_GFp_simple_point_set_affine_coordinates(const EC_GROUP *, EC_POINT *,
const BIGNUM *x, const BIGNUM *y,
BN_CTX *);
diff --git a/src/crypto/fipsmodule/ec/p224-64.c b/src/crypto/fipsmodule/ec/p224-64.c
index 26a94b9..ba25d22 100644
--- a/src/crypto/fipsmodule/ec/p224-64.c
+++ b/src/crypto/fipsmodule/ec/p224-64.c
@@ -1038,14 +1038,13 @@
}
static int ec_GFp_nistp224_points_mul(const EC_GROUP *group, EC_POINT *r,
- const BIGNUM *g_scalar, const EC_POINT *p,
- const BIGNUM *p_scalar, BN_CTX *ctx) {
+ const EC_SCALAR *g_scalar,
+ const EC_POINT *p,
+ const EC_SCALAR *p_scalar, BN_CTX *ctx) {
int ret = 0;
BN_CTX *new_ctx = NULL;
BIGNUM *x, *y, *z, *tmp_scalar;
- p224_felem_bytearray g_secret, p_secret;
p224_felem p_pre_comp[17][3];
- p224_felem_bytearray tmp;
p224_felem x_in, y_in, z_in, x_out, y_out, z_out;
if (ctx == NULL) {
@@ -1067,23 +1066,7 @@
if (p != NULL && p_scalar != NULL) {
// We treat NULL scalars as 0, and NULL points as points at infinity, i.e.,
// they contribute nothing to the linear combination.
- OPENSSL_memset(&p_secret, 0, sizeof(p_secret));
OPENSSL_memset(&p_pre_comp, 0, sizeof(p_pre_comp));
- size_t num_bytes;
- // reduce g_scalar to 0 <= g_scalar < 2^224
- if (BN_num_bits(p_scalar) > 224 || BN_is_negative(p_scalar)) {
- // this is an unusual input, and we don't guarantee
- // constant-timeness
- if (!BN_nnmod(tmp_scalar, p_scalar, &group->order, ctx)) {
- OPENSSL_PUT_ERROR(EC, ERR_R_BN_LIB);
- goto err;
- }
- num_bytes = BN_bn2bin(tmp_scalar, tmp);
- } else {
- num_bytes = BN_bn2bin(p_scalar, tmp);
- }
-
- p224_flip_endian(p_secret, tmp, num_bytes);
// precompute multiples
if (!p224_BN_to_felem(x_out, &p->X) ||
!p224_BN_to_felem(y_out, &p->Y) ||
@@ -1109,26 +1092,10 @@
}
}
- if (g_scalar != NULL) {
- OPENSSL_memset(g_secret, 0, sizeof(g_secret));
- size_t num_bytes;
- // reduce g_scalar to 0 <= g_scalar < 2^224
- if (BN_num_bits(g_scalar) > 224 || BN_is_negative(g_scalar)) {
- // this is an unusual input, and we don't guarantee constant-timeness
- if (!BN_nnmod(tmp_scalar, g_scalar, &group->order, ctx)) {
- OPENSSL_PUT_ERROR(EC, ERR_R_BN_LIB);
- goto err;
- }
- num_bytes = BN_bn2bin(tmp_scalar, tmp);
- } else {
- num_bytes = BN_bn2bin(g_scalar, tmp);
- }
-
- p224_flip_endian(g_secret, tmp, num_bytes);
- }
- p224_batch_mul(
- x_out, y_out, z_out, (p != NULL && p_scalar != NULL) ? p_secret : NULL,
- g_scalar != NULL ? g_secret : NULL, (const p224_felem(*)[3])p_pre_comp);
+ p224_batch_mul(x_out, y_out, z_out,
+ (p != NULL && p_scalar != NULL) ? p_scalar->bytes : NULL,
+ g_scalar != NULL ? g_scalar->bytes : NULL,
+ (const p224_felem(*)[3])p_pre_comp);
// reduce the output to its unique minimal representation
p224_felem_contract(x_in, x_out);
diff --git a/src/crypto/fipsmodule/ec/p256-64.c b/src/crypto/fipsmodule/ec/p256-64.c
index 04ae56b..d4a8ff6 100644
--- a/src/crypto/fipsmodule/ec/p256-64.c
+++ b/src/crypto/fipsmodule/ec/p256-64.c
@@ -1582,14 +1582,13 @@
}
static int ec_GFp_nistp256_points_mul(const EC_GROUP *group, EC_POINT *r,
- const BIGNUM *g_scalar, const EC_POINT *p,
- const BIGNUM *p_scalar, BN_CTX *ctx) {
+ const EC_SCALAR *g_scalar,
+ const EC_POINT *p,
+ const EC_SCALAR *p_scalar, BN_CTX *ctx) {
int ret = 0;
BN_CTX *new_ctx = NULL;
BIGNUM *x, *y, *z, *tmp_scalar;
- felem_bytearray g_secret, p_secret;
smallfelem p_pre_comp[17][3];
- felem_bytearray tmp;
smallfelem x_in, y_in, z_in;
felem x_out, y_out, z_out;
@@ -1611,21 +1610,7 @@
if (p != NULL && p_scalar != NULL) {
// We treat NULL scalars as 0, and NULL points as points at infinity, i.e.,
// they contribute nothing to the linear combination.
- OPENSSL_memset(&p_secret, 0, sizeof(p_secret));
OPENSSL_memset(&p_pre_comp, 0, sizeof(p_pre_comp));
- size_t num_bytes;
- // Reduce g_scalar to 0 <= g_scalar < 2^256.
- if (BN_num_bits(p_scalar) > 256 || BN_is_negative(p_scalar)) {
- // This is an unusual input, and we don't guarantee constant-timeness.
- if (!BN_nnmod(tmp_scalar, p_scalar, &group->order, ctx)) {
- OPENSSL_PUT_ERROR(EC, ERR_R_BN_LIB);
- goto err;
- }
- num_bytes = BN_bn2bin(tmp_scalar, tmp);
- } else {
- num_bytes = BN_bn2bin(p_scalar, tmp);
- }
- flip_endian(p_secret, tmp, num_bytes);
// Precompute multiples.
if (!BN_to_felem(x_out, &p->X) ||
!BN_to_felem(y_out, &p->Y) ||
@@ -1650,28 +1635,10 @@
}
}
- if (g_scalar != NULL) {
- size_t num_bytes;
-
- OPENSSL_memset(g_secret, 0, sizeof(g_secret));
- // reduce g_scalar to 0 <= g_scalar < 2^256
- if (BN_num_bits(g_scalar) > 256 || BN_is_negative(g_scalar)) {
- // this is an unusual input, and we don't guarantee
- // constant-timeness.
- if (!BN_nnmod(tmp_scalar, g_scalar, &group->order, ctx)) {
- OPENSSL_PUT_ERROR(EC, ERR_R_BN_LIB);
- goto err;
- }
- num_bytes = BN_bn2bin(tmp_scalar, tmp);
- } else {
- num_bytes = BN_bn2bin(g_scalar, tmp);
- }
- flip_endian(g_secret, tmp, num_bytes);
- }
batch_mul(x_out, y_out, z_out,
- (p != NULL && p_scalar != NULL) ? p_secret : NULL,
- g_scalar != NULL ? g_secret : NULL,
- (const smallfelem(*)[3]) &p_pre_comp);
+ (p != NULL && p_scalar != NULL) ? p_scalar->bytes : NULL,
+ g_scalar != NULL ? g_scalar->bytes : NULL,
+ (const smallfelem(*)[3]) & p_pre_comp);
// reduce the output to its unique minimal representation
felem_contract(x_in, x_out);
diff --git a/src/crypto/fipsmodule/ec/p256-x86_64.c b/src/crypto/fipsmodule/ec/p256-x86_64.c
index 0004add..a9b603a 100644
--- a/src/crypto/fipsmodule/ec/p256-x86_64.c
+++ b/src/crypto/fipsmodule/ec/p256-x86_64.c
@@ -216,8 +216,8 @@
// r = p * p_scalar
static int ecp_nistz256_windowed_mul(const EC_GROUP *group, P256_POINT *r,
- const EC_POINT *p, const BIGNUM *p_scalar,
- BN_CTX *ctx) {
+ const EC_POINT *p,
+ const EC_SCALAR *p_scalar) {
assert(p != NULL);
assert(p_scalar != NULL);
@@ -229,55 +229,8 @@
// ~1599 ((96 * 16) + 63) bytes of stack space.
alignas(64) P256_POINT table[16];
uint8_t p_str[33];
-
-
- int ret = 0;
- BN_CTX *new_ctx = NULL;
- int ctx_started = 0;
-
- if (BN_num_bits(p_scalar) > 256 || BN_is_negative(p_scalar)) {
- if (ctx == NULL) {
- new_ctx = BN_CTX_new();
- if (new_ctx == NULL) {
- OPENSSL_PUT_ERROR(EC, ERR_R_MALLOC_FAILURE);
- goto err;
- }
- ctx = new_ctx;
- }
- BN_CTX_start(ctx);
- ctx_started = 1;
- BIGNUM *mod = BN_CTX_get(ctx);
- if (mod == NULL) {
- OPENSSL_PUT_ERROR(EC, ERR_R_MALLOC_FAILURE);
- goto err;
- }
- if (!BN_nnmod(mod, p_scalar, &group->order, ctx)) {
- OPENSSL_PUT_ERROR(EC, ERR_R_BN_LIB);
- goto err;
- }
- p_scalar = mod;
- }
-
- int j;
- for (j = 0; j < p_scalar->top * BN_BYTES; j += BN_BYTES) {
- BN_ULONG d = p_scalar->d[j / BN_BYTES];
-
- p_str[j + 0] = d & 0xff;
- p_str[j + 1] = (d >> 8) & 0xff;
- p_str[j + 2] = (d >> 16) & 0xff;
- p_str[j + 3] = (d >>= 24) & 0xff;
- if (BN_BYTES == 8) {
- d >>= 8;
- p_str[j + 4] = d & 0xff;
- p_str[j + 5] = (d >> 8) & 0xff;
- p_str[j + 6] = (d >> 16) & 0xff;
- p_str[j + 7] = (d >> 24) & 0xff;
- }
- }
-
- for (; j < 33; j++) {
- p_str[j] = 0;
- }
+ OPENSSL_memcpy(p_str, p_scalar->bytes, 32);
+ p_str[32] = 0;
// table[0] is implicitly (0,0,0) (the point at infinity), therefore it is
// not stored. All other values are actually stored with an offset of -1 in
@@ -288,7 +241,7 @@
!ecp_nistz256_bignum_to_field_elem(row[1 - 1].Y, &p->Y) ||
!ecp_nistz256_bignum_to_field_elem(row[1 - 1].Z, &p->Z)) {
OPENSSL_PUT_ERROR(EC, EC_R_COORDINATES_OUT_OF_RANGE);
- goto err;
+ return 0;
}
ecp_nistz256_point_double(&row[2 - 1], &row[1 - 1]);
@@ -354,19 +307,13 @@
ecp_nistz256_point_add(r, r, &h);
- ret = 1;
-
-err:
- if (ctx_started) {
- BN_CTX_end(ctx);
- }
- BN_CTX_free(new_ctx);
- return ret;
+ return 1;
}
-static int ecp_nistz256_points_mul(
- const EC_GROUP *group, EC_POINT *r, const BIGNUM *g_scalar,
- const EC_POINT *p_, const BIGNUM *p_scalar, BN_CTX *ctx) {
+static int ecp_nistz256_points_mul(const EC_GROUP *group, EC_POINT *r,
+ const EC_SCALAR *g_scalar,
+ const EC_POINT *p_,
+ const EC_SCALAR *p_scalar, BN_CTX *ctx) {
assert((p_ != NULL) == (p_scalar != NULL));
static const unsigned kWindowSize = 7;
@@ -377,54 +324,10 @@
P256_POINT_AFFINE a;
} t, p;
- int ret = 0;
- BN_CTX *new_ctx = NULL;
- int ctx_started = 0;
-
if (g_scalar != NULL) {
- if (BN_num_bits(g_scalar) > 256 || BN_is_negative(g_scalar)) {
- if (ctx == NULL) {
- new_ctx = BN_CTX_new();
- if (new_ctx == NULL) {
- goto err;
- }
- ctx = new_ctx;
- }
- BN_CTX_start(ctx);
- ctx_started = 1;
- BIGNUM *tmp_scalar = BN_CTX_get(ctx);
- if (tmp_scalar == NULL) {
- goto err;
- }
-
- if (!BN_nnmod(tmp_scalar, g_scalar, &group->order, ctx)) {
- OPENSSL_PUT_ERROR(EC, ERR_R_BN_LIB);
- goto err;
- }
- g_scalar = tmp_scalar;
- }
-
- uint8_t p_str[33] = {0};
- int i;
- for (i = 0; i < g_scalar->top * BN_BYTES; i += BN_BYTES) {
- BN_ULONG d = g_scalar->d[i / BN_BYTES];
-
- p_str[i + 0] = d & 0xff;
- p_str[i + 1] = (d >> 8) & 0xff;
- p_str[i + 2] = (d >> 16) & 0xff;
- p_str[i + 3] = (d >>= 24) & 0xff;
- if (BN_BYTES == 8) {
- d >>= 8;
- p_str[i + 4] = d & 0xff;
- p_str[i + 5] = (d >> 8) & 0xff;
- p_str[i + 6] = (d >> 16) & 0xff;
- p_str[i + 7] = (d >> 24) & 0xff;
- }
- }
-
- for (; i < (int) sizeof(p_str); i++) {
- p_str[i] = 0;
- }
+ uint8_t p_str[33];
+ OPENSSL_memcpy(p_str, g_scalar->bytes, 32);
+ p_str[32] = 0;
// First window
unsigned wvalue = (p_str[0] << 1) & kMask;
@@ -445,7 +348,7 @@
OPENSSL_memset(p.p.Z, 0, sizeof(p.p.Z));
copy_conditional(p.p.Z, ONE, is_not_zero(wvalue >> 1));
- for (i = 1; i < 37; i++) {
+ for (int i = 1; i < 37; i++) {
unsigned off = (index - 1) / 8;
wvalue = p_str[off] | p_str[off + 1] << 8;
wvalue = (wvalue >> ((index - 1) % 8)) & kMask;
@@ -469,8 +372,8 @@
out = &p.p;
}
- if (!ecp_nistz256_windowed_mul(group, out, p_, p_scalar, ctx)) {
- goto err;
+ if (!ecp_nistz256_windowed_mul(group, out, p_, p_scalar)) {
+ return 0;
}
if (!p_is_infinity) {
@@ -485,14 +388,7 @@
return 0;
}
- ret = 1;
-
-err:
- if (ctx_started) {
- BN_CTX_end(ctx);
- }
- BN_CTX_free(new_ctx);
- return ret;
+ return 1;
}
static int ecp_nistz256_get_affine(const EC_GROUP *group, const EC_POINT *point,
diff --git a/src/crypto/fipsmodule/ec/simple.c b/src/crypto/fipsmodule/ec/simple.c
index f7faa45..bc39525 100644
--- a/src/crypto/fipsmodule/ec/simple.c
+++ b/src/crypto/fipsmodule/ec/simple.c
@@ -295,49 +295,6 @@
return ret;
}
-int ec_GFp_simple_get_Jprojective_coordinates_GFp(const EC_GROUP *group,
- const EC_POINT *point,
- BIGNUM *x, BIGNUM *y,
- BIGNUM *z, BN_CTX *ctx) {
- BN_CTX *new_ctx = NULL;
- int ret = 0;
-
- if (group->meth->field_decode != 0) {
- if (ctx == NULL) {
- ctx = new_ctx = BN_CTX_new();
- if (ctx == NULL) {
- return 0;
- }
- }
-
- if (x != NULL && !group->meth->field_decode(group, x, &point->X, ctx)) {
- goto err;
- }
- if (y != NULL && !group->meth->field_decode(group, y, &point->Y, ctx)) {
- goto err;
- }
- if (z != NULL && !group->meth->field_decode(group, z, &point->Z, ctx)) {
- goto err;
- }
- } else {
- if (x != NULL && !BN_copy(x, &point->X)) {
- goto err;
- }
- if (y != NULL && !BN_copy(y, &point->Y)) {
- goto err;
- }
- if (z != NULL && !BN_copy(z, &point->Z)) {
- goto err;
- }
- }
-
- ret = 1;
-
-err:
- BN_CTX_free(new_ctx);
- return ret;
-}
-
int ec_GFp_simple_point_set_affine_coordinates(const EC_GROUP *group,
EC_POINT *point, const BIGNUM *x,
const BIGNUM *y, BN_CTX *ctx) {
diff --git a/src/crypto/fipsmodule/ec/wnaf.c b/src/crypto/fipsmodule/ec/wnaf.c
index 842a8fb..e3b6437 100644
--- a/src/crypto/fipsmodule/ec/wnaf.c
+++ b/src/crypto/fipsmodule/ec/wnaf.c
@@ -122,11 +122,6 @@
sign = -1;
}
- if (scalar->d == NULL || scalar->top == 0) {
- OPENSSL_PUT_ERROR(EC, ERR_R_INTERNAL_ERROR);
- goto err;
- }
-
len = BN_num_bits(scalar);
// The modified wNAF may be one digit longer than binary representation
// (*ret_len will be set to the actual length, i.e. at most
@@ -236,8 +231,9 @@
return 1;
}
-int ec_wNAF_mul(const EC_GROUP *group, EC_POINT *r, const BIGNUM *g_scalar,
- const EC_POINT *p, const BIGNUM *p_scalar, BN_CTX *ctx) {
+int ec_wNAF_mul(const EC_GROUP *group, EC_POINT *r,
+ const EC_SCALAR *g_scalar_raw, const EC_POINT *p,
+ const EC_SCALAR *p_scalar_raw, BN_CTX *ctx) {
BN_CTX *new_ctx = NULL;
const EC_POINT *generator = NULL;
EC_POINT *tmp = NULL;
@@ -262,13 +258,32 @@
goto err;
}
}
+ BN_CTX_start(ctx);
+
+ // Convert from |EC_SCALAR| to |BIGNUM|. |BIGNUM| is not constant-time, but
+ // neither is the rest of this function.
+ BIGNUM *g_scalar = NULL, *p_scalar = NULL;
+ if (g_scalar_raw != NULL) {
+ g_scalar = BN_CTX_get(ctx);
+ if (g_scalar == NULL ||
+ !bn_set_words(g_scalar, g_scalar_raw->words, group->order.top)) {
+ goto err;
+ }
+ }
+ if (p_scalar_raw != NULL) {
+ p_scalar = BN_CTX_get(ctx);
+ if (p_scalar == NULL ||
+ !bn_set_words(p_scalar, p_scalar_raw->words, group->order.top)) {
+ goto err;
+ }
+ }
// TODO: This function used to take |points| and |scalars| as arrays of
// |num| elements. The code below should be simplified to work in terms of |p|
// and |p_scalar|.
size_t num = p != NULL ? 1 : 0;
const EC_POINT **points = p != NULL ? &p : NULL;
- const BIGNUM **scalars = p != NULL ? &p_scalar : NULL;
+ BIGNUM **scalars = p != NULL ? &p_scalar : NULL;
total_num = num;
@@ -433,6 +448,9 @@
ret = 1;
err:
+ if (ctx != NULL) {
+ BN_CTX_end(ctx);
+ }
BN_CTX_free(new_ctx);
EC_POINT_free(tmp);
OPENSSL_free(wsize);
diff --git a/src/crypto/fipsmodule/ecdsa/ecdsa.c b/src/crypto/fipsmodule/ecdsa/ecdsa.c
index 0e2adb6..319a934 100644
--- a/src/crypto/fipsmodule/ecdsa/ecdsa.c
+++ b/src/crypto/fipsmodule/ecdsa/ecdsa.c
@@ -58,37 +58,72 @@
#include <openssl/bn.h>
#include <openssl/err.h>
#include <openssl/mem.h>
+#include <openssl/sha.h>
+#include <openssl/type_check.h>
#include "../bn/internal.h"
#include "../ec/internal.h"
#include "../../internal.h"
-// digest_to_bn interprets |digest_len| bytes from |digest| as a big-endian
-// number and sets |out| to that value. It then truncates |out| so that it's,
-// at most, as long as |order|. It returns one on success and zero otherwise.
-static int digest_to_bn(BIGNUM *out, const uint8_t *digest, size_t digest_len,
- const BIGNUM *order) {
- size_t num_bits;
-
- num_bits = BN_num_bits(order);
- // Need to truncate digest if it is too long: first truncate whole
- // bytes.
+// digest_to_scalar interprets |digest_len| bytes from |digest| as a scalar for
+// ECDSA. Note this value is not fully reduced modulo the order, only the
+// correct number of bits.
+static void digest_to_scalar(const EC_GROUP *group, EC_SCALAR *out,
+ const uint8_t *digest, size_t digest_len) {
+ const BIGNUM *order = &group->order;
+ size_t num_bits = BN_num_bits(order);
+ // Need to truncate digest if it is too long: first truncate whole bytes.
if (8 * digest_len > num_bits) {
digest_len = (num_bits + 7) / 8;
}
- if (!BN_bin2bn(digest, digest_len, out)) {
- OPENSSL_PUT_ERROR(ECDSA, ERR_R_BN_LIB);
- return 0;
+ OPENSSL_memset(out, 0, sizeof(EC_SCALAR));
+ for (size_t i = 0; i < digest_len; i++) {
+ out->bytes[i] = digest[digest_len - 1 - i];
}
// If still too long truncate remaining bits with a shift
- if ((8 * digest_len > num_bits) &&
- !BN_rshift(out, out, 8 - (num_bits & 0x7))) {
- OPENSSL_PUT_ERROR(ECDSA, ERR_R_BN_LIB);
+ if (8 * digest_len > num_bits) {
+ size_t shift = 8 - (num_bits & 0x7);
+ for (int i = 0; i < order->top - 1; i++) {
+ out->words[i] =
+ (out->words[i] >> shift) | (out->words[i + 1] << (BN_BITS2 - shift));
+ }
+ out->words[order->top - 1] >>= shift;
+ }
+}
+
+// field_element_to_scalar reduces |r| modulo |group->order|. |r| must
+// previously have been reduced modulo |group->field|.
+static int field_element_to_scalar(const EC_GROUP *group, BIGNUM *r) {
+ // We must have p < 2×order, assuming p is not tiny (p >= 17). Thus rather we
+ // can reduce by performing at most one subtraction.
+ //
+ // Proof: We only work with prime order curves, so the number of points on
+ // the curve is the order. Thus Hasse's theorem gives:
+ //
+ // |order - (p + 1)| <= 2×sqrt(p)
+ // p + 1 - order <= 2×sqrt(p)
+ // p + 1 - 2×sqrt(p) <= order
+ // p + 1 - 2×(p/4) < order (p/4 > sqrt(p) for p >= 17)
+ // p/2 < p/2 + 1 < order
+ // p < 2×order
+ //
+ // Additionally, one can manually check this property for built-in curves. It
+ // is enforced for legacy custom curves in |EC_GROUP_set_generator|.
+ //
+ // TODO(davidben): Introduce |EC_FIELD_ELEMENT|, make this a function from
+ // |EC_FIELD_ELEMENT| to |EC_SCALAR|, and cut out the |BIGNUM|. Does this need
+ // to be constant-time for signing? |r| is the x-coordinate for kG, which is
+ // public unless k was rerolled because |s| was zero.
+ assert(!BN_is_negative(r));
+ assert(BN_cmp(r, &group->field) < 0);
+ if (BN_cmp(r, &group->order) >= 0 &&
+ !BN_sub(r, r, &group->order)) {
return 0;
}
-
+ assert(!BN_is_negative(r));
+ assert(BN_cmp(r, &group->order) < 0);
return 1;
}
@@ -116,67 +151,87 @@
OPENSSL_free(sig);
}
-ECDSA_SIG *ECDSA_do_sign(const uint8_t *digest, size_t digest_len,
- const EC_KEY *key) {
- return ECDSA_do_sign_ex(digest, digest_len, NULL, NULL, key);
+void ECDSA_SIG_get0(const ECDSA_SIG *sig, const BIGNUM **out_r,
+ const BIGNUM **out_s) {
+ if (out_r != NULL) {
+ *out_r = sig->r;
+ }
+ if (out_s != NULL) {
+ *out_s = sig->s;
+ }
+}
+
+int ECDSA_SIG_set0(ECDSA_SIG *sig, BIGNUM *r, BIGNUM *s) {
+ if (r == NULL || s == NULL) {
+ return 0;
+ }
+ BN_free(sig->r);
+ BN_free(sig->s);
+ sig->r = r;
+ sig->s = s;
+ return 1;
}
int ECDSA_do_verify(const uint8_t *digest, size_t digest_len,
const ECDSA_SIG *sig, const EC_KEY *eckey) {
- int ret = 0;
- BN_CTX *ctx;
- BIGNUM *u1, *u2, *m, *X;
- EC_POINT *point = NULL;
- const EC_GROUP *group;
- const EC_POINT *pub_key;
-
- // check input values
- if ((group = EC_KEY_get0_group(eckey)) == NULL ||
- (pub_key = EC_KEY_get0_public_key(eckey)) == NULL ||
- sig == NULL) {
+ const EC_GROUP *group = EC_KEY_get0_group(eckey);
+ const EC_POINT *pub_key = EC_KEY_get0_public_key(eckey);
+ if (group == NULL || pub_key == NULL || sig == NULL) {
OPENSSL_PUT_ERROR(ECDSA, ECDSA_R_MISSING_PARAMETERS);
return 0;
}
- ctx = BN_CTX_new();
+ BN_CTX *ctx = BN_CTX_new();
if (!ctx) {
OPENSSL_PUT_ERROR(ECDSA, ERR_R_MALLOC_FAILURE);
return 0;
}
+ int ret = 0;
+ EC_POINT *point = NULL;
BN_CTX_start(ctx);
- u1 = BN_CTX_get(ctx);
- u2 = BN_CTX_get(ctx);
- m = BN_CTX_get(ctx);
- X = BN_CTX_get(ctx);
- if (u1 == NULL || u2 == NULL || m == NULL || X == NULL) {
+ BIGNUM *X = BN_CTX_get(ctx);
+ if (X == NULL) {
OPENSSL_PUT_ERROR(ECDSA, ERR_R_BN_LIB);
goto err;
}
+ EC_SCALAR r, s, m, u1, u2, s_inv_mont;
const BIGNUM *order = EC_GROUP_get0_order(group);
- if (BN_is_zero(sig->r) || BN_is_negative(sig->r) ||
- BN_ucmp(sig->r, order) >= 0 || BN_is_zero(sig->s) ||
- BN_is_negative(sig->s) || BN_ucmp(sig->s, order) >= 0) {
+ if (BN_is_zero(sig->r) ||
+ BN_is_negative(sig->r) ||
+ BN_ucmp(sig->r, order) >= 0 ||
+ !ec_bignum_to_scalar(group, &r, sig->r) ||
+ BN_is_zero(sig->s) ||
+ BN_is_negative(sig->s) ||
+ BN_ucmp(sig->s, order) >= 0 ||
+ !ec_bignum_to_scalar(group, &s, sig->s)) {
OPENSSL_PUT_ERROR(ECDSA, ECDSA_R_BAD_SIGNATURE);
goto err;
}
- // tmp = inv(s) mod order
+ // s_inv_mont = s^-1 mod order. We convert the result to Montgomery form for
+ // the products below.
int no_inverse;
- if (!BN_mod_inverse_odd(u2, &no_inverse, sig->s, order, ctx)) {
- OPENSSL_PUT_ERROR(ECDSA, ERR_R_BN_LIB);
+ if (!BN_mod_inverse_odd(X, &no_inverse, sig->s, order, ctx) ||
+ !ec_bignum_to_scalar(group, &s_inv_mont, X) ||
+ !bn_to_montgomery_small(s_inv_mont.words, order->top, s_inv_mont.words,
+ order->top, group->order_mont)) {
goto err;
}
- if (!digest_to_bn(m, digest, digest_len, order)) {
- goto err;
- }
- // u1 = m * tmp mod order
- if (!BN_mod_mul(u1, m, u2, order, ctx)) {
- OPENSSL_PUT_ERROR(ECDSA, ERR_R_BN_LIB);
- goto err;
- }
- // u2 = r * tmp mod order
- if (!BN_mod_mul(u2, sig->r, u2, order, ctx)) {
- OPENSSL_PUT_ERROR(ECDSA, ERR_R_BN_LIB);
+ // u1 = m * s_inv_mont mod order
+ // u2 = r * s_inv_mont mod order
+ //
+ // |s_inv_mont| is in Montgomery form while |m| and |r| are not, so |u1| and
+ // |u2| will be taken out of Montgomery form, as desired. Note that, although
+ // |m| is not fully reduced, |bn_mod_mul_montgomery_small| only requires the
+ // product not exceed R * |order|. |s_inv_mont| is fully reduced and |m| <
+ // 2^BN_num_bits(order) <= R, so this holds.
+ digest_to_scalar(group, &m, digest, digest_len);
+ if (!bn_mod_mul_montgomery_small(u1.words, order->top, m.words, order->top,
+ s_inv_mont.words, order->top,
+ group->order_mont) ||
+ !bn_mod_mul_montgomery_small(u2.words, order->top, r.words, order->top,
+ s_inv_mont.words, order->top,
+ group->order_mont)) {
goto err;
}
@@ -185,7 +240,7 @@
OPENSSL_PUT_ERROR(ECDSA, ERR_R_MALLOC_FAILURE);
goto err;
}
- if (!EC_POINT_mul(group, point, u1, pub_key, u2, ctx)) {
+ if (!ec_point_mul_scalar(group, point, &u1, pub_key, &u2, ctx)) {
OPENSSL_PUT_ERROR(ECDSA, ERR_R_EC_LIB);
goto err;
}
@@ -193,12 +248,12 @@
OPENSSL_PUT_ERROR(ECDSA, ERR_R_EC_LIB);
goto err;
}
- if (!BN_nnmod(u1, X, order, ctx)) {
+ if (!field_element_to_scalar(group, X)) {
OPENSSL_PUT_ERROR(ECDSA, ERR_R_BN_LIB);
goto err;
}
- // if the signature is correct u1 is equal to sig->r
- if (BN_ucmp(u1, sig->r) != 0) {
+ // The signature is correct iff |X| is equal to |sig->r|.
+ if (BN_ucmp(X, sig->r) != 0) {
OPENSSL_PUT_ERROR(ECDSA, ECDSA_R_BAD_SIGNATURE);
goto err;
}
@@ -212,45 +267,26 @@
return ret;
}
-static int ecdsa_sign_setup(const EC_KEY *eckey, BN_CTX *ctx_in, BIGNUM **kinvp,
- BIGNUM **rp, const uint8_t *digest,
- size_t digest_len) {
- BN_CTX *ctx = NULL;
- BIGNUM *k = NULL, *kinv = NULL, *r = NULL, *tmp = NULL;
+static int ecdsa_sign_setup(const EC_KEY *eckey, BN_CTX *ctx,
+ EC_SCALAR *out_kinv_mont, BIGNUM **rp,
+ const uint8_t *digest, size_t digest_len,
+ const EC_SCALAR *priv_key) {
EC_POINT *tmp_point = NULL;
- const EC_GROUP *group;
int ret = 0;
-
- if (eckey == NULL || (group = EC_KEY_get0_group(eckey)) == NULL) {
- OPENSSL_PUT_ERROR(ECDSA, ERR_R_PASSED_NULL_PARAMETER);
- return 0;
- }
-
- if (ctx_in == NULL) {
- if ((ctx = BN_CTX_new()) == NULL) {
- OPENSSL_PUT_ERROR(ECDSA, ERR_R_MALLOC_FAILURE);
- return 0;
- }
- } else {
- ctx = ctx_in;
- }
-
- k = BN_new();
- kinv = BN_new(); // this value is later returned in *kinvp
- r = BN_new(); // this value is later returned in *rp
- tmp = BN_new();
- if (k == NULL || kinv == NULL || r == NULL || tmp == NULL) {
+ EC_SCALAR k;
+ BIGNUM *r = BN_new(); // this value is later returned in *rp
+ if (r == NULL) {
OPENSSL_PUT_ERROR(ECDSA, ERR_R_MALLOC_FAILURE);
goto err;
}
+ const EC_GROUP *group = EC_KEY_get0_group(eckey);
+ const BIGNUM *order = EC_GROUP_get0_order(group);
tmp_point = EC_POINT_new(group);
if (tmp_point == NULL) {
OPENSSL_PUT_ERROR(ECDSA, ERR_R_EC_LIB);
goto err;
}
- const BIGNUM *order = EC_GROUP_get0_order(group);
-
// Check that the size of the group order is FIPS compliant (FIPS 186-4
// B.5.2).
if (BN_num_bits(order) < 160) {
@@ -259,169 +295,130 @@
}
do {
- // If possible, we'll include the private key and message digest in the k
- // generation. The |digest| argument is only empty if |ECDSA_sign_setup| is
- // being used.
+ // Include the private key and message digest in the k generation.
if (eckey->fixed_k != NULL) {
- if (!BN_copy(k, eckey->fixed_k)) {
+ if (!ec_bignum_to_scalar(group, &k, eckey->fixed_k)) {
goto err;
}
- } else if (digest_len > 0) {
- if (!BN_generate_dsa_nonce(k, order, EC_KEY_get0_private_key(eckey),
- digest, digest_len, ctx)) {
- OPENSSL_PUT_ERROR(ECDSA, ECDSA_R_RANDOM_NUMBER_GENERATION_FAILED);
- goto err;
- }
- } else if (!BN_rand_range_ex(k, 1, order)) {
- OPENSSL_PUT_ERROR(ECDSA, ECDSA_R_RANDOM_NUMBER_GENERATION_FAILED);
- goto err;
- }
-
- // Compute the inverse of k. The order is a prime, so use Fermat's Little
- // Theorem. Note |ec_group_get_order_mont| may return NULL but
- // |bn_mod_inverse_prime| allows this.
- if (!bn_mod_inverse_prime(kinv, k, order, ctx,
- ec_group_get_order_mont(group))) {
- OPENSSL_PUT_ERROR(ECDSA, ERR_R_BN_LIB);
- goto err;
- }
-
- // We do not want timing information to leak the length of k,
- // so we compute G*k using an equivalent scalar of fixed
- // bit-length.
-
- if (!BN_add(k, k, order)) {
- goto err;
- }
- if (BN_num_bits(k) <= BN_num_bits(order)) {
- if (!BN_add(k, k, order)) {
+ } else {
+ // Pass a SHA512 hash of the private key and digest as additional data
+ // into the RBG. This is a hardening measure against entropy failure.
+ OPENSSL_COMPILE_ASSERT(SHA512_DIGEST_LENGTH >= 32,
+ additional_data_is_too_large_for_sha512);
+ SHA512_CTX sha;
+ uint8_t additional_data[SHA512_DIGEST_LENGTH];
+ SHA512_Init(&sha);
+ SHA512_Update(&sha, priv_key->words, order->top * sizeof(BN_ULONG));
+ SHA512_Update(&sha, digest, digest_len);
+ SHA512_Final(additional_data, &sha);
+ if (!ec_random_nonzero_scalar(group, &k, additional_data)) {
goto err;
}
}
- // compute r the x-coordinate of generator * k
- if (!EC_POINT_mul(group, tmp_point, k, NULL, NULL, ctx)) {
- OPENSSL_PUT_ERROR(ECDSA, ERR_R_EC_LIB);
+ // Compute k^-1. We leave it in the Montgomery domain as an optimization for
+ // later operations.
+ if (!bn_to_montgomery_small(out_kinv_mont->words, order->top, k.words,
+ order->top, group->order_mont) ||
+ !bn_mod_inverse_prime_mont_small(out_kinv_mont->words, order->top,
+ out_kinv_mont->words, order->top,
+ group->order_mont)) {
goto err;
}
- if (!EC_POINT_get_affine_coordinates_GFp(group, tmp_point, tmp, NULL,
+
+ // Compute r, the x-coordinate of generator * k.
+ if (!ec_point_mul_scalar(group, tmp_point, &k, NULL, NULL, ctx) ||
+ !EC_POINT_get_affine_coordinates_GFp(group, tmp_point, r, NULL,
ctx)) {
- OPENSSL_PUT_ERROR(ECDSA, ERR_R_EC_LIB);
goto err;
}
- if (!BN_nnmod(r, tmp, order, ctx)) {
- OPENSSL_PUT_ERROR(ECDSA, ERR_R_BN_LIB);
+ if (!field_element_to_scalar(group, r)) {
goto err;
}
} while (BN_is_zero(r));
- // clear old values if necessary
BN_clear_free(*rp);
- BN_clear_free(*kinvp);
-
- // save the pre-computed values
*rp = r;
- *kinvp = kinv;
+ r = NULL;
ret = 1;
err:
- BN_clear_free(k);
- if (!ret) {
- BN_clear_free(kinv);
- BN_clear_free(r);
- }
- if (ctx_in == NULL) {
- BN_CTX_free(ctx);
- }
+ OPENSSL_cleanse(&k, sizeof(k));
+ BN_clear_free(r);
EC_POINT_free(tmp_point);
- BN_clear_free(tmp);
return ret;
}
-int ECDSA_sign_setup(const EC_KEY *eckey, BN_CTX *ctx, BIGNUM **kinv,
- BIGNUM **rp) {
- return ecdsa_sign_setup(eckey, ctx, kinv, rp, NULL, 0);
-}
-
-ECDSA_SIG *ECDSA_do_sign_ex(const uint8_t *digest, size_t digest_len,
- const BIGNUM *in_kinv, const BIGNUM *in_r,
- const EC_KEY *eckey) {
- int ok = 0;
- BIGNUM *kinv = NULL, *s, *m = NULL, *tmp = NULL;
- const BIGNUM *ckinv;
- BN_CTX *ctx = NULL;
- const EC_GROUP *group;
- ECDSA_SIG *ret;
- const BIGNUM *priv_key;
-
+ECDSA_SIG *ECDSA_do_sign(const uint8_t *digest, size_t digest_len,
+ const EC_KEY *eckey) {
if (eckey->ecdsa_meth && eckey->ecdsa_meth->sign) {
OPENSSL_PUT_ERROR(ECDSA, ECDSA_R_NOT_IMPLEMENTED);
return NULL;
}
- group = EC_KEY_get0_group(eckey);
- priv_key = EC_KEY_get0_private_key(eckey);
-
- if (group == NULL || priv_key == NULL) {
+ const EC_GROUP *group = EC_KEY_get0_group(eckey);
+ const BIGNUM *priv_key_bn = EC_KEY_get0_private_key(eckey);
+ if (group == NULL || priv_key_bn == NULL) {
OPENSSL_PUT_ERROR(ECDSA, ERR_R_PASSED_NULL_PARAMETER);
return NULL;
}
+ const BIGNUM *order = EC_GROUP_get0_order(group);
- ret = ECDSA_SIG_new();
- if (!ret) {
+ int ok = 0;
+ ECDSA_SIG *ret = ECDSA_SIG_new();
+ BN_CTX *ctx = BN_CTX_new();
+ EC_SCALAR kinv_mont, priv_key, r_mont, s, tmp, m;
+ if (ret == NULL || ctx == NULL) {
OPENSSL_PUT_ERROR(ECDSA, ERR_R_MALLOC_FAILURE);
return NULL;
}
- s = ret->s;
- if ((ctx = BN_CTX_new()) == NULL ||
- (tmp = BN_new()) == NULL ||
- (m = BN_new()) == NULL) {
- OPENSSL_PUT_ERROR(ECDSA, ERR_R_MALLOC_FAILURE);
- goto err;
- }
-
- const BIGNUM *order = EC_GROUP_get0_order(group);
-
- if (!digest_to_bn(m, digest, digest_len, order)) {
+ digest_to_scalar(group, &m, digest, digest_len);
+ if (!ec_bignum_to_scalar(group, &priv_key, priv_key_bn)) {
goto err;
}
for (;;) {
- if (in_kinv == NULL || in_r == NULL) {
- if (!ecdsa_sign_setup(eckey, ctx, &kinv, &ret->r, digest, digest_len)) {
- OPENSSL_PUT_ERROR(ECDSA, ERR_R_ECDSA_LIB);
- goto err;
- }
- ckinv = kinv;
- } else {
- ckinv = in_kinv;
- if (BN_copy(ret->r, in_r) == NULL) {
- OPENSSL_PUT_ERROR(ECDSA, ERR_R_MALLOC_FAILURE);
- goto err;
- }
+ if (!ecdsa_sign_setup(eckey, ctx, &kinv_mont, &ret->r, digest, digest_len,
+ &priv_key)) {
+ goto err;
}
- if (!BN_mod_mul(tmp, priv_key, ret->r, order, ctx)) {
- OPENSSL_PUT_ERROR(ECDSA, ERR_R_BN_LIB);
+ // Compute priv_key * r (mod order). Note if only one parameter is in the
+ // Montgomery domain, |bn_mod_mul_montgomery_small| will compute the answer
+ // in the normal domain.
+ if (!ec_bignum_to_scalar(group, &r_mont, ret->r) ||
+ !bn_to_montgomery_small(r_mont.words, order->top, r_mont.words,
+ order->top, group->order_mont) ||
+ !bn_mod_mul_montgomery_small(s.words, order->top, priv_key.words,
+ order->top, r_mont.words, order->top,
+ group->order_mont)) {
goto err;
}
- if (!BN_mod_add_quick(s, tmp, m, order)) {
- OPENSSL_PUT_ERROR(ECDSA, ERR_R_BN_LIB);
+
+ // Compute s += m in constant time. Reduce one copy of |order| if necessary.
+ // Note this does not leave |s| fully reduced. We have
+ // |m| < 2^BN_num_bits(order), so subtracting |order| leaves
+ // 0 <= |s| < 2^BN_num_bits(order).
+ BN_ULONG carry = bn_add_words(s.words, s.words, m.words, order->top);
+ BN_ULONG v = bn_sub_words(tmp.words, s.words, order->d, order->top) - carry;
+ v = 0u - v;
+ for (int i = 0; i < order->top; i++) {
+ s.words[i] = constant_time_select_w(v, s.words[i], tmp.words[i]);
+ }
+
+ // Finally, multiply s by k^-1. That was retained in Montgomery form, so the
+ // same technique as the previous multiplication works. Although the
+ // previous step did not fully reduce |s|, |bn_mod_mul_montgomery_small|
+ // only requires the product not exceed R * |order|. |kinv_mont| is fully
+ // reduced and |s| < 2^BN_num_bits(order) <= R, so this holds.
+ if (!bn_mod_mul_montgomery_small(s.words, order->top, s.words, order->top,
+ kinv_mont.words, order->top,
+ group->order_mont) ||
+ !bn_set_words(ret->s, s.words, order->top)) {
goto err;
}
- if (!BN_mod_mul(s, s, ckinv, order, ctx)) {
- OPENSSL_PUT_ERROR(ECDSA, ERR_R_BN_LIB);
- goto err;
- }
- if (BN_is_zero(s)) {
- // if kinv and r have been supplied by the caller
- // don't to generate new kinv and r values
- if (in_kinv != NULL && in_r != NULL) {
- OPENSSL_PUT_ERROR(ECDSA, ECDSA_R_NEED_NEW_SETUP_VALUES);
- goto err;
- }
- } else {
+ if (!BN_is_zero(ret->s)) {
// s != 0 => we have a valid signature
break;
}
@@ -435,8 +432,11 @@
ret = NULL;
}
BN_CTX_free(ctx);
- BN_clear_free(m);
- BN_clear_free(tmp);
- BN_clear_free(kinv);
+ OPENSSL_cleanse(&kinv_mont, sizeof(kinv_mont));
+ OPENSSL_cleanse(&priv_key, sizeof(priv_key));
+ OPENSSL_cleanse(&r_mont, sizeof(r_mont));
+ OPENSSL_cleanse(&s, sizeof(s));
+ OPENSSL_cleanse(&tmp, sizeof(tmp));
+ OPENSSL_cleanse(&m, sizeof(m));
return ret;
}
diff --git a/src/crypto/fipsmodule/ecdsa/ecdsa_test.cc b/src/crypto/fipsmodule/ecdsa/ecdsa_test.cc
index 837b95d..258c128 100644
--- a/src/crypto/fipsmodule/ecdsa/ecdsa_test.cc
+++ b/src/crypto/fipsmodule/ecdsa/ecdsa_test.cc
@@ -64,6 +64,7 @@
#include <openssl/nid.h>
#include <openssl/rand.h>
+#include "../ec/internal.h"
#include "../../test/file_test.h"
@@ -282,6 +283,32 @@
return nullptr;
}
+static bssl::UniquePtr<EC_GROUP> MakeCustomClone(const EC_GROUP *group) {
+ bssl::UniquePtr<BN_CTX> ctx(BN_CTX_new());
+ bssl::UniquePtr<BIGNUM> p(BN_new()), a(BN_new()), b(BN_new()), x(BN_new()),
+ y(BN_new());
+ if (!ctx || !p || !a || !b || !x || !y ||
+ !EC_GROUP_get_curve_GFp(group, p.get(), a.get(), b.get(), ctx.get()) ||
+ !EC_POINT_get_affine_coordinates_GFp(
+ group, EC_GROUP_get0_generator(group), x.get(), y.get(), ctx.get())) {
+ return nullptr;
+ }
+ bssl::UniquePtr<EC_GROUP> ret(
+ EC_GROUP_new_curve_GFp(p.get(), a.get(), b.get(), ctx.get()));
+ if (!ret) {
+ return nullptr;
+ }
+ bssl::UniquePtr<EC_POINT> g(EC_POINT_new(ret.get()));
+ if (!g ||
+ !EC_POINT_set_affine_coordinates_GFp(ret.get(), g.get(), x.get(), y.get(),
+ ctx.get()) ||
+ !EC_GROUP_set_generator(ret.get(), g.get(), EC_GROUP_get0_order(group),
+ BN_value_one())) {
+ return nullptr;
+ }
+ return ret;
+}
+
static bssl::UniquePtr<BIGNUM> GetBIGNUM(FileTest *t, const char *key) {
std::vector<uint8_t> bytes;
if (!t->GetBytes(&bytes, key)) {
@@ -294,80 +321,90 @@
TEST(ECDSATest, VerifyTestVectors) {
FileTestGTest("crypto/fipsmodule/ecdsa/ecdsa_verify_tests.txt",
[](FileTest *t) {
- bssl::UniquePtr<EC_GROUP> group = GetCurve(t, "Curve");
- ASSERT_TRUE(group);
- bssl::UniquePtr<BIGNUM> x = GetBIGNUM(t, "X");
- ASSERT_TRUE(x);
- bssl::UniquePtr<BIGNUM> y = GetBIGNUM(t, "Y");
- ASSERT_TRUE(y);
- bssl::UniquePtr<BIGNUM> r = GetBIGNUM(t, "R");
- ASSERT_TRUE(r);
- bssl::UniquePtr<BIGNUM> s = GetBIGNUM(t, "S");
- ASSERT_TRUE(s);
- std::vector<uint8_t> digest;
- ASSERT_TRUE(t->GetBytes(&digest, "Digest"));
+ for (bool custom_group : {false, true}) {
+ SCOPED_TRACE(custom_group);
+ bssl::UniquePtr<EC_GROUP> group = GetCurve(t, "Curve");
+ ASSERT_TRUE(group);
+ if (custom_group) {
+ group = MakeCustomClone(group.get());
+ ASSERT_TRUE(group);
+ }
+ bssl::UniquePtr<BIGNUM> x = GetBIGNUM(t, "X");
+ ASSERT_TRUE(x);
+ bssl::UniquePtr<BIGNUM> y = GetBIGNUM(t, "Y");
+ ASSERT_TRUE(y);
+ bssl::UniquePtr<BIGNUM> r = GetBIGNUM(t, "R");
+ ASSERT_TRUE(r);
+ bssl::UniquePtr<BIGNUM> s = GetBIGNUM(t, "S");
+ ASSERT_TRUE(s);
+ std::vector<uint8_t> digest;
+ ASSERT_TRUE(t->GetBytes(&digest, "Digest"));
- bssl::UniquePtr<EC_KEY> key(EC_KEY_new());
- ASSERT_TRUE(key);
- bssl::UniquePtr<EC_POINT> pub_key(EC_POINT_new(group.get()));
- ASSERT_TRUE(pub_key);
- bssl::UniquePtr<ECDSA_SIG> sig(ECDSA_SIG_new());
- ASSERT_TRUE(sig);
- ASSERT_TRUE(EC_KEY_set_group(key.get(), group.get()));
- ASSERT_TRUE(EC_POINT_set_affine_coordinates_GFp(group.get(), pub_key.get(),
- x.get(), y.get(), nullptr));
- ASSERT_TRUE(EC_KEY_set_public_key(key.get(), pub_key.get()));
- ASSERT_TRUE(BN_copy(sig->r, r.get()));
- ASSERT_TRUE(BN_copy(sig->s, s.get()));
+ bssl::UniquePtr<EC_KEY> key(EC_KEY_new());
+ ASSERT_TRUE(key);
+ bssl::UniquePtr<EC_POINT> pub_key(EC_POINT_new(group.get()));
+ ASSERT_TRUE(pub_key);
+ bssl::UniquePtr<ECDSA_SIG> sig(ECDSA_SIG_new());
+ ASSERT_TRUE(sig);
+ ASSERT_TRUE(EC_KEY_set_group(key.get(), group.get()));
+ ASSERT_TRUE(EC_POINT_set_affine_coordinates_GFp(
+ group.get(), pub_key.get(), x.get(), y.get(), nullptr));
+ ASSERT_TRUE(EC_KEY_set_public_key(key.get(), pub_key.get()));
+ ASSERT_TRUE(BN_copy(sig->r, r.get()));
+ ASSERT_TRUE(BN_copy(sig->s, s.get()));
- EXPECT_EQ(
- t->HasAttribute("Invalid") ? 0 : 1,
- ECDSA_do_verify(digest.data(), digest.size(), sig.get(), key.get()));
+ EXPECT_EQ(
+ t->HasAttribute("Invalid") ? 0 : 1,
+ ECDSA_do_verify(digest.data(), digest.size(), sig.get(), key.get()));
+ }
});
}
TEST(ECDSATest, SignTestVectors) {
FileTestGTest("crypto/fipsmodule/ecdsa/ecdsa_sign_tests.txt",
[](FileTest *t) {
- bssl::UniquePtr<EC_GROUP> group = GetCurve(t, "Curve");
- ASSERT_TRUE(group);
- bssl::UniquePtr<BIGNUM> priv_key = GetBIGNUM(t, "Private");
- ASSERT_TRUE(priv_key);
- bssl::UniquePtr<BIGNUM> x = GetBIGNUM(t, "X");
- ASSERT_TRUE(x);
- bssl::UniquePtr<BIGNUM> y = GetBIGNUM(t, "Y");
- ASSERT_TRUE(y);
- bssl::UniquePtr<BIGNUM> k = GetBIGNUM(t, "K");
- ASSERT_TRUE(k);
- bssl::UniquePtr<BIGNUM> r = GetBIGNUM(t, "R");
- ASSERT_TRUE(r);
- bssl::UniquePtr<BIGNUM> s = GetBIGNUM(t, "S");
- ASSERT_TRUE(s);
- std::vector<uint8_t> digest;
- ASSERT_TRUE(t->GetBytes(&digest, "Digest"));
+ for (bool custom_group : {false, true}) {
+ SCOPED_TRACE(custom_group);
+ bssl::UniquePtr<EC_GROUP> group = GetCurve(t, "Curve");
+ ASSERT_TRUE(group);
+ if (custom_group) {
+ group = MakeCustomClone(group.get());
+ ASSERT_TRUE(group);
+ }
+ bssl::UniquePtr<BIGNUM> priv_key = GetBIGNUM(t, "Private");
+ ASSERT_TRUE(priv_key);
+ bssl::UniquePtr<BIGNUM> x = GetBIGNUM(t, "X");
+ ASSERT_TRUE(x);
+ bssl::UniquePtr<BIGNUM> y = GetBIGNUM(t, "Y");
+ ASSERT_TRUE(y);
+ bssl::UniquePtr<BIGNUM> k = GetBIGNUM(t, "K");
+ ASSERT_TRUE(k);
+ bssl::UniquePtr<BIGNUM> r = GetBIGNUM(t, "R");
+ ASSERT_TRUE(r);
+ bssl::UniquePtr<BIGNUM> s = GetBIGNUM(t, "S");
+ ASSERT_TRUE(s);
+ std::vector<uint8_t> digest;
+ ASSERT_TRUE(t->GetBytes(&digest, "Digest"));
- bssl::UniquePtr<EC_KEY> key(EC_KEY_new());
- ASSERT_TRUE(key);
- bssl::UniquePtr<EC_POINT> pub_key(EC_POINT_new(group.get()));
- ASSERT_TRUE(pub_key);
- ASSERT_TRUE(EC_KEY_set_group(key.get(), group.get()));
- ASSERT_TRUE(EC_KEY_set_private_key(key.get(), priv_key.get()));
- ASSERT_TRUE(EC_POINT_set_affine_coordinates_GFp(group.get(), pub_key.get(),
- x.get(), y.get(), nullptr));
- ASSERT_TRUE(EC_KEY_set_public_key(key.get(), pub_key.get()));
- ASSERT_TRUE(EC_KEY_check_key(key.get()));
+ bssl::UniquePtr<EC_KEY> key(EC_KEY_new());
+ ASSERT_TRUE(key);
+ bssl::UniquePtr<EC_POINT> pub_key(EC_POINT_new(group.get()));
+ ASSERT_TRUE(pub_key);
+ ASSERT_TRUE(EC_KEY_set_group(key.get(), group.get()));
+ ASSERT_TRUE(EC_KEY_set_private_key(key.get(), priv_key.get()));
+ ASSERT_TRUE(EC_POINT_set_affine_coordinates_GFp(
+ group.get(), pub_key.get(), x.get(), y.get(), nullptr));
+ ASSERT_TRUE(EC_KEY_set_public_key(key.get(), pub_key.get()));
+ ASSERT_TRUE(EC_KEY_check_key(key.get()));
- // |ECDSA_do_sign_ex| expects |k| to already be inverted.
- bssl::UniquePtr<BN_CTX> ctx(BN_CTX_new());
- ASSERT_TRUE(ctx);
- ASSERT_TRUE(BN_mod_inverse(k.get(), k.get(),
- EC_GROUP_get0_order(group.get()), ctx.get()));
+ // Set the fixed k for testing purposes.
+ key->fixed_k = k.release();
+ bssl::UniquePtr<ECDSA_SIG> sig(
+ ECDSA_do_sign(digest.data(), digest.size(), key.get()));
+ ASSERT_TRUE(sig);
- bssl::UniquePtr<ECDSA_SIG> sig(ECDSA_do_sign_ex(
- digest.data(), digest.size(), k.get(), r.get(), key.get()));
- ASSERT_TRUE(sig);
-
- EXPECT_EQ(0, BN_cmp(r.get(), sig->r));
- EXPECT_EQ(0, BN_cmp(s.get(), sig->s));
+ EXPECT_EQ(0, BN_cmp(r.get(), sig->r));
+ EXPECT_EQ(0, BN_cmp(s.get(), sig->s));
+ }
});
}
diff --git a/src/crypto/fipsmodule/rsa/internal.h b/src/crypto/fipsmodule/rsa/internal.h
index 67f2cb9..0f0c763 100644
--- a/src/crypto/fipsmodule/rsa/internal.h
+++ b/src/crypto/fipsmodule/rsa/internal.h
@@ -119,10 +119,6 @@
extern const BN_ULONG kBoringSSLRSASqrtTwo[];
extern const size_t kBoringSSLRSASqrtTwoLen;
-// rsa_less_than_words returns one if |a| < |b| and zero otherwise, where |a|
-// and |b| both are |len| words long. It runs in constant time.
-int rsa_less_than_words(const BN_ULONG *a, const BN_ULONG *b, size_t len);
-
// rsa_greater_than_pow2 returns one if |b| is greater than 2^|n| and zero
// otherwise.
int rsa_greater_than_pow2(const BIGNUM *b, int n);
diff --git a/src/crypto/fipsmodule/rsa/rsa.c b/src/crypto/fipsmodule/rsa/rsa.c
index d001d7b..4a84314 100644
--- a/src/crypto/fipsmodule/rsa/rsa.c
+++ b/src/crypto/fipsmodule/rsa/rsa.c
@@ -157,6 +157,8 @@
return 1;
}
+unsigned RSA_bits(const RSA *rsa) { return BN_num_bits(rsa->n); }
+
void RSA_get0_key(const RSA *rsa, const BIGNUM **out_n, const BIGNUM **out_e,
const BIGNUM **out_d) {
if (out_n != NULL) {
diff --git a/src/crypto/fipsmodule/rsa/rsa_impl.c b/src/crypto/fipsmodule/rsa/rsa_impl.c
index adbb69f..fb27320 100644
--- a/src/crypto/fipsmodule/rsa/rsa_impl.c
+++ b/src/crypto/fipsmodule/rsa/rsa_impl.c
@@ -775,19 +775,6 @@
};
const size_t kBoringSSLRSASqrtTwoLen = OPENSSL_ARRAY_SIZE(kBoringSSLRSASqrtTwo);
-int rsa_less_than_words(const BN_ULONG *a, const BN_ULONG *b, size_t len) {
- OPENSSL_COMPILE_ASSERT(sizeof(BN_ULONG) <= sizeof(crypto_word_t),
- crypto_word_t_too_small);
- int ret = 0;
- // Process the words in little-endian order.
- for (size_t i = 0; i < len; i++) {
- crypto_word_t eq = constant_time_eq_w(a[i], b[i]);
- crypto_word_t lt = constant_time_lt_w(a[i], b[i]);
- ret = constant_time_select_int(eq, ret, constant_time_select_int(lt, 1, 0));
- }
- return ret;
-}
-
int rsa_greater_than_pow2(const BIGNUM *b, int n) {
if (BN_is_negative(b) || n == INT_MAX) {
return 0;
@@ -866,7 +853,7 @@
if (to_check > out_len) {
to_check = out_len;
}
- if (!rsa_less_than_words(
+ if (!bn_less_than_words(
kBoringSSLRSASqrtTwo + kBoringSSLRSASqrtTwoLen - to_check,
out->d + out_len - to_check, to_check)) {
continue;
diff --git a/src/crypto/rsa_extra/rsa_test.cc b/src/crypto/rsa_extra/rsa_test.cc
index 23e8c67..97b32bf 100644
--- a/src/crypto/rsa_extra/rsa_test.cc
+++ b/src/crypto/rsa_extra/rsa_test.cc
@@ -735,58 +735,6 @@
EXPECT_EQ(3072u / 2u, bits);
}
-TEST(RSATest, LessThanWords) {
- // kTestVectors is an array of 256-bit values in sorted order.
- static const BN_ULONG kTestVectors[][256 / BN_BITS2] = {
- {TOBN(0x00000000, 0x00000000), TOBN(0x00000000, 0x00000000),
- TOBN(0x00000000, 0x00000000), TOBN(0x00000000, 0x00000000)},
- {TOBN(0x00000000, 0x00000001), TOBN(0x00000000, 0x00000000),
- TOBN(0x00000000, 0x00000000), TOBN(0x00000000, 0x00000000)},
- {TOBN(0xffffffff, 0xffffffff), TOBN(0x00000000, 0x00000000),
- TOBN(0x00000000, 0x00000000), TOBN(0x00000000, 0x00000000)},
- {TOBN(0xffffffff, 0xffffffff), TOBN(0xffffffff, 0xffffffff),
- TOBN(0x00000000, 0x00000000), TOBN(0x00000000, 0x00000000)},
- {TOBN(0xffffffff, 0xffffffff), TOBN(0xffffffff, 0xffffffff),
- TOBN(0xffffffff, 0xffffffff), TOBN(0x00000000, 0x00000000)},
- {TOBN(0x00000000, 0x00000000), TOBN(0x1d6f60ba, 0x893ba84c),
- TOBN(0x597d89b3, 0x754abe9f), TOBN(0xb504f333, 0xf9de6484)},
- {TOBN(0x00000000, 0x83339915), TOBN(0x1d6f60ba, 0x893ba84c),
- TOBN(0x597d89b3, 0x754abe9f), TOBN(0xb504f333, 0xf9de6484)},
- {TOBN(0xed17ac85, 0x00000000), TOBN(0x1d6f60ba, 0x893ba84c),
- TOBN(0x597d89b3, 0x754abe9f), TOBN(0xb504f333, 0xf9de6484)},
- {TOBN(0xed17ac85, 0x83339915), TOBN(0x1d6f60ba, 0x893ba84c),
- TOBN(0x597d89b3, 0x754abe9f), TOBN(0xb504f333, 0xf9de6484)},
- {TOBN(0xed17ac85, 0xffffffff), TOBN(0x1d6f60ba, 0x893ba84c),
- TOBN(0x597d89b3, 0x754abe9f), TOBN(0xb504f333, 0xf9de6484)},
- {TOBN(0xffffffff, 0x83339915), TOBN(0x1d6f60ba, 0x893ba84c),
- TOBN(0x597d89b3, 0x754abe9f), TOBN(0xb504f333, 0xf9de6484)},
- {TOBN(0xffffffff, 0xffffffff), TOBN(0x1d6f60ba, 0x893ba84c),
- TOBN(0x597d89b3, 0x754abe9f), TOBN(0xb504f333, 0xf9de6484)},
- {TOBN(0x00000000, 0x00000000), TOBN(0x00000000, 0x00000000),
- TOBN(0x00000000, 0x00000000), TOBN(0xffffffff, 0xffffffff)},
- {TOBN(0x00000000, 0x00000000), TOBN(0x00000000, 0x00000000),
- TOBN(0xffffffff, 0xffffffff), TOBN(0xffffffff, 0xffffffff)},
- {TOBN(0x00000000, 0x00000001), TOBN(0x00000000, 0x00000000),
- TOBN(0xffffffff, 0xffffffff), TOBN(0xffffffff, 0xffffffff)},
- {TOBN(0x00000000, 0x00000000), TOBN(0xffffffff, 0xffffffff),
- TOBN(0xffffffff, 0xffffffff), TOBN(0xffffffff, 0xffffffff)},
- {TOBN(0xffffffff, 0xffffffff), TOBN(0xffffffff, 0xffffffff),
- TOBN(0xffffffff, 0xffffffff), TOBN(0xffffffff, 0xffffffff)},
- };
-
- for (size_t i = 0; i < OPENSSL_ARRAY_SIZE(kTestVectors); i++) {
- SCOPED_TRACE(i);
- for (size_t j = 0; j < OPENSSL_ARRAY_SIZE(kTestVectors); j++) {
- SCOPED_TRACE(j);
- EXPECT_EQ(i < j ? 1 : 0,
- rsa_less_than_words(kTestVectors[i], kTestVectors[j],
- OPENSSL_ARRAY_SIZE(kTestVectors[i])));
- }
- }
-
- EXPECT_EQ(0, rsa_less_than_words(NULL, NULL, 0));
-}
-
TEST(RSATest, GreaterThanPow2) {
bssl::UniquePtr<BIGNUM> b(BN_new());
BN_zero(b.get());
diff --git a/src/crypto/x509/x509_test.cc b/src/crypto/x509/x509_test.cc
index 2fb1b1b..cd4e61d 100644
--- a/src/crypto/x509/x509_test.cc
+++ b/src/crypto/x509/x509_test.cc
@@ -834,7 +834,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(CBS_ASN1_SEQUENCE, enc_pointer[0]);
+ ASSERT_EQ(0x30, enc_pointer[0]);
}
TEST(X509Test, TestFromBufferWithTrailingData) {
diff --git a/src/crypto/x509/x_algor.c b/src/crypto/x509/x_algor.c
index abacd06..13c9a8c 100644
--- a/src/crypto/x509/x_algor.c
+++ b/src/crypto/x509/x_algor.c
@@ -105,8 +105,8 @@
return 1;
}
-void X509_ALGOR_get0(ASN1_OBJECT **paobj, int *pptype, void **ppval,
- X509_ALGOR *algor)
+void X509_ALGOR_get0(const ASN1_OBJECT **paobj, int *pptype, const void **ppval,
+ const X509_ALGOR *algor)
{
if (paobj)
*paobj = algor->algorithm;
diff --git a/src/crypto/x509v3/v3_lib.c b/src/crypto/x509v3/v3_lib.c
index c4718e3..8f5435d 100644
--- a/src/crypto/x509v3/v3_lib.c
+++ b/src/crypto/x509v3/v3_lib.c
@@ -288,9 +288,9 @@
int X509V3_add1_i2d(STACK_OF(X509_EXTENSION) **x, int nid, void *value,
int crit, unsigned long flags)
{
- int extidx = -1;
- int errcode;
- X509_EXTENSION *ext, *extmp;
+ int errcode, extidx = -1;
+ X509_EXTENSION *ext = NULL, *extmp;
+ STACK_OF(X509_EXTENSION) *ret = NULL;
unsigned long ext_op = flags & X509V3_ADD_OP_MASK;
/*
@@ -348,13 +348,21 @@
return 1;
}
- if (!*x && !(*x = sk_X509_EXTENSION_new_null()))
- return -1;
- if (!sk_X509_EXTENSION_push(*x, ext))
- return -1;
+ if ((ret = *x) == NULL
+ && (ret = sk_X509_EXTENSION_new_null()) == NULL)
+ goto m_fail;
+ if (!sk_X509_EXTENSION_push(ret, ext))
+ goto m_fail;
+ *x = ret;
return 1;
+ m_fail:
+ if (ret != *x)
+ sk_X509_EXTENSION_free(ret);
+ X509_EXTENSION_free(ext);
+ return -1;
+
err:
if (!(flags & X509V3_ADD_SILENT))
OPENSSL_PUT_ERROR(X509V3, errcode);
diff --git a/src/include/openssl/base.h b/src/include/openssl/base.h
index adb5047..cc962f3 100644
--- a/src/include/openssl/base.h
+++ b/src/include/openssl/base.h
@@ -151,7 +151,7 @@
// A consumer may use this symbol in the preprocessor to temporarily build
// against multiple revisions of BoringSSL at the same time. It is not
// recommended to do so for longer than is necessary.
-#define BORINGSSL_API_VERSION 4
+#define BORINGSSL_API_VERSION 6
#if defined(BORINGSSL_SHARED_LIBRARY)
diff --git a/src/include/openssl/bn.h b/src/include/openssl/bn.h
index 9960b75..bb32c2f 100644
--- a/src/include/openssl/bn.h
+++ b/src/include/openssl/bn.h
@@ -487,8 +487,8 @@
// zero on allocation failure.
OPENSSL_EXPORT int BN_clear_bit(BIGNUM *a, int n);
-// BN_is_bit_set returns the value of the |n|th, least-significant bit in |a|,
-// or zero if the bit doesn't exist.
+// BN_is_bit_set returns one if the |n|th least-significant bit in |a| exists
+// and is set. Otherwise, it returns zero.
OPENSSL_EXPORT int BN_is_bit_set(const BIGNUM *a, int n);
// BN_mask_bits truncates |a| so that it is only |n| bits long. It returns one
@@ -618,17 +618,6 @@
// BN_pseudo_rand_range is an alias for BN_rand_range.
OPENSSL_EXPORT int BN_pseudo_rand_range(BIGNUM *rnd, const BIGNUM *range);
-// BN_generate_dsa_nonce generates a random number 0 <= out < range. Unlike
-// BN_rand_range, it also includes the contents of |priv| and |message| in the
-// generation so that an RNG failure isn't fatal as long as |priv| remains
-// secret. This is intended for use in DSA and ECDSA where an RNG weakness
-// leads directly to private key exposure unless this function is used.
-// It returns one on success and zero on error.
-OPENSSL_EXPORT int BN_generate_dsa_nonce(BIGNUM *out, const BIGNUM *range,
- const BIGNUM *priv,
- const uint8_t *message,
- size_t message_len, BN_CTX *ctx);
-
// BN_GENCB holds a callback function that is used by generation functions that
// can take a very long time to complete. Use |BN_GENCB_set| to initialise a
// |BN_GENCB| structure.
diff --git a/src/include/openssl/bytestring.h b/src/include/openssl/bytestring.h
index 6d355b5..3906809 100644
--- a/src/include/openssl/bytestring.h
+++ b/src/include/openssl/bytestring.h
@@ -164,34 +164,36 @@
#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
+#define CBS_ASN1_CONSTRUCTED (0x20u << 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
+// 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)
-// CBS_ASN1_CLASS_MASK may be ANDed with a tag to query its class.
-#define CBS_ASN1_CLASS_MASK 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_TAG_NUMBER_MASK may be ANDed with a tag to query its number.
-#define CBS_ASN1_TAG_NUMBER_MASK 0x1fu
+#define CBS_ASN1_TAG_NUMBER_MASK ((1u << (5 + CBS_ASN1_TAG_SHIFT)) - 1)
// 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
@@ -209,16 +211,12 @@
// (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);
@@ -396,9 +394,7 @@
// 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. 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.
+// the object. 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
diff --git a/src/include/openssl/dsa.h b/src/include/openssl/dsa.h
index 315e7ca..2966f9d 100644
--- a/src/include/openssl/dsa.h
+++ b/src/include/openssl/dsa.h
@@ -172,7 +172,7 @@
// DSA_do_sign returns a signature of the hash in |digest| by the key in |dsa|
// and returns an allocated, DSA_SIG structure, or NULL on error.
OPENSSL_EXPORT DSA_SIG *DSA_do_sign(const uint8_t *digest, size_t digest_len,
- DSA *dsa);
+ const DSA *dsa);
// DSA_do_verify verifies that |sig| is a valid signature, by the public key in
// |dsa|, of the hash in |digest|. It returns one if so, zero if invalid and -1
@@ -212,7 +212,7 @@
// (The |type| argument is ignored.)
OPENSSL_EXPORT int DSA_sign(int type, const uint8_t *digest, size_t digest_len,
uint8_t *out_sig, unsigned int *out_siglen,
- DSA *dsa);
+ const DSA *dsa);
// DSA_verify verifies that |sig| is a valid, ASN.1 signature, by the public
// key in |dsa|, of the hash in |digest|. It returns one if so, zero if invalid
@@ -284,19 +284,6 @@
OPENSSL_EXPORT int DSA_marshal_parameters(CBB *cbb, const DSA *dsa);
-// Precomputation.
-
-// DSA_sign_setup precomputes the message independent part of the DSA signature
-// and writes them to |*out_kinv| and |*out_r|. Returns one on success, zero on
-// error.
-//
-// TODO(fork): decide what to do with this. Since making DSA* opaque there's no
-// way for the user to install them. Also, it forces the DSA* not to be const
-// when passing to the signing function.
-OPENSSL_EXPORT int DSA_sign_setup(const DSA *dsa, BN_CTX *ctx,
- BIGNUM **out_kinv, BIGNUM **out_r);
-
-
// Conversion.
// DSA_dup_DH returns a |DH| constructed from the parameters of |dsa|. This is
@@ -411,9 +398,6 @@
BIGNUM *pub_key; // y public key
BIGNUM *priv_key; // x private key
- BIGNUM *kinv; // Signing pre-calc
- BIGNUM *r; // Signing pre-calc
-
int flags;
// Normally used to cache montgomery values
CRYPTO_MUTEX method_mont_lock;
diff --git a/src/include/openssl/ec.h b/src/include/openssl/ec.h
index dee41b7..b34605f 100644
--- a/src/include/openssl/ec.h
+++ b/src/include/openssl/ec.h
@@ -402,5 +402,6 @@
#define EC_R_GROUP_MISMATCH 130
#define EC_R_INVALID_COFACTOR 131
#define EC_R_PUBLIC_KEY_VALIDATION_FAILED 132
+#define EC_R_INVALID_SCALAR 133
#endif // OPENSSL_HEADER_EC_H
diff --git a/src/include/openssl/ecdsa.h b/src/include/openssl/ecdsa.h
index ff26fe4..42da1c6 100644
--- a/src/include/openssl/ecdsa.h
+++ b/src/include/openssl/ecdsa.h
@@ -106,6 +106,16 @@
// ECDSA_SIG_free frees |sig| its member |BIGNUM|s.
OPENSSL_EXPORT void ECDSA_SIG_free(ECDSA_SIG *sig);
+// ECDSA_SIG_get0 sets |*out_r| and |*out_s|, if non-NULL, to the two
+// components of |sig|.
+OPENSSL_EXPORT void ECDSA_SIG_get0(const ECDSA_SIG *sig, const BIGNUM **out_r,
+ const BIGNUM **out_s);
+
+// ECDSA_SIG_set0 sets |sig|'s components to |r| and |s|, neither of which may
+// be NULL. On success, it takes ownership of each argument and returns one.
+// Otherwise, it returns zero.
+OPENSSL_EXPORT int ECDSA_SIG_set0(ECDSA_SIG *sig, BIGNUM *r, BIGNUM *s);
+
// ECDSA_do_sign signs |digest_len| bytes from |digest| with |key| and returns
// the resulting signature structure, or NULL on error.
OPENSSL_EXPORT ECDSA_SIG *ECDSA_do_sign(const uint8_t *digest,
@@ -118,34 +128,6 @@
const ECDSA_SIG *sig, const EC_KEY *key);
-// Signing with precomputation.
-//
-// Parts of the ECDSA signature can be independent of the message to be signed
-// thus it's possible to precompute them and reduce the signing latency.
-//
-// TODO(fork): remove support for this as it cannot support safe-randomness.
-
-// ECDSA_sign_setup precomputes parts of an ECDSA signing operation. It sets
-// |*kinv| and |*rp| to the precomputed values and uses the |ctx| argument, if
-// not NULL. It returns one on success and zero otherwise.
-OPENSSL_EXPORT int ECDSA_sign_setup(const EC_KEY *eckey, BN_CTX *ctx,
- BIGNUM **kinv, BIGNUM **rp);
-
-// ECDSA_do_sign_ex is the same as |ECDSA_do_sign| but takes precomputed values
-// as generated by |ECDSA_sign_setup|.
-OPENSSL_EXPORT ECDSA_SIG *ECDSA_do_sign_ex(const uint8_t *digest,
- size_t digest_len,
- const BIGNUM *kinv, const BIGNUM *rp,
- const EC_KEY *eckey);
-
-// ECDSA_sign_ex is the same as |ECDSA_sign| but takes precomputed values as
-// generated by |ECDSA_sign_setup|.
-OPENSSL_EXPORT int ECDSA_sign_ex(int type, const uint8_t *digest,
- size_t digest_len, uint8_t *sig,
- unsigned int *sig_len, const BIGNUM *kinv,
- const BIGNUM *rp, const EC_KEY *eckey);
-
-
// ASN.1 functions.
// ECDSA_SIG_parse parses a DER-encoded ECDSA-Sig-Value structure from |cbs| and
diff --git a/src/include/openssl/rsa.h b/src/include/openssl/rsa.h
index 74268cf..11aa8e4 100644
--- a/src/include/openssl/rsa.h
+++ b/src/include/openssl/rsa.h
@@ -89,6 +89,9 @@
// Properties.
+// RSA_bits returns the size of |rsa|, in bits.
+OPENSSL_EXPORT unsigned RSA_bits(const RSA *rsa);
+
// RSA_get0_key sets |*out_n|, |*out_e|, and |*out_d|, if non-NULL, to |rsa|'s
// modulus, public exponent, and private exponent, respectively. If |rsa| is a
// public key, the private exponent will be set to NULL.
diff --git a/src/include/openssl/ssl.h b/src/include/openssl/ssl.h
index 8c36ad5..53a8eb5 100644
--- a/src/include/openssl/ssl.h
+++ b/src/include/openssl/ssl.h
@@ -1125,16 +1125,7 @@
// key hooks. This is used to off-load signing operations to a custom,
// potentially asynchronous, backend. Metadata about the key such as the type
// and size are parsed out of the certificate.
-//
-// TODO(davidben): This API has a number of legacy hooks. Remove the last
-// consumer of |sign_digest| and trim it.
struct ssl_private_key_method_st {
- // type is ignored and should be NULL.
- int (*type)(SSL *ssl);
-
- // max_signature_len is ignored and should be NULL.
- size_t (*max_signature_len)(SSL *ssl);
-
// sign signs the message |in| in using the specified signature algorithm. On
// success, it returns |ssl_private_key_success| and writes at most |max_out|
// bytes of signature data to |out| and sets |*out_len| to the number of bytes
@@ -1156,30 +1147,6 @@
uint16_t signature_algorithm,
const uint8_t *in, size_t in_len);
- // sign_digest signs |in_len| bytes of digest from |in|. |md| is the hash
- // function used to calculate |in|. On success, it returns
- // |ssl_private_key_success| and writes at most |max_out| bytes of signature
- // data to |out|. On failure, it returns |ssl_private_key_failure|. If the
- // operation has not completed, it returns |ssl_private_key_retry|. |sign|
- // should arrange for the high-level operation on |ssl| to be retried when the
- // operation is completed. This will result in a call to |complete|.
- //
- // If the key is an RSA key, implementations must use PKCS#1 padding. |in| is
- // the digest itself, so the DigestInfo prefix, if any, must be prepended by
- // |sign|. If |md| is |EVP_md5_sha1|, there is no prefix.
- //
- // It is an error to call |sign_digest| while another private key operation is
- // in progress on |ssl|.
- //
- // This function is deprecated. Implement |sign| instead.
- //
- // TODO(davidben): Remove this function.
- enum ssl_private_key_result_t (*sign_digest)(SSL *ssl, uint8_t *out,
- size_t *out_len, size_t max_out,
- const EVP_MD *md,
- const uint8_t *in,
- size_t in_len);
-
// decrypt decrypts |in_len| bytes of encrypted data from |in|. On success it
// returns |ssl_private_key_success|, writes at most |max_out| bytes of
// decrypted data to |out| and sets |*out_len| to the actual number of bytes
@@ -3978,18 +3945,6 @@
OPENSSL_EXPORT int SSL_add_dir_cert_subjects_to_stack(STACK_OF(X509_NAME) *out,
const char *dir);
-// SSL_set_private_key_digest_prefs copies |num_digests| NIDs from |digest_nids|
-// into |ssl|. These digests will be used, in decreasing order of preference,
-// when signing with |ssl|'s private key. It returns one on success and zero on
-// error.
-//
-// Use |SSL_set_signing_algorithm_prefs| instead.
-//
-// TODO(davidben): Remove this API when callers have been updated.
-OPENSSL_EXPORT int SSL_set_private_key_digest_prefs(SSL *ssl,
- const int *digest_nids,
- size_t num_digests);
-
// SSL_set_verify_result calls |abort| unless |result| is |X509_V_OK|.
//
// TODO(davidben): Remove this function once it has been removed from
@@ -4599,6 +4554,7 @@
#define SSL_R_UNEXPECTED_EXTENSION_ON_EARLY_DATA 279
#define SSL_R_NO_SUPPORTED_VERSIONS_ENABLED 280
#define SSL_R_APPLICATION_DATA_INSTEAD_OF_HANDSHAKE 281
+#define SSL_R_EMPTY_HELLO_RETRY_REQUEST 282
#define SSL_R_SSLV3_ALERT_CLOSE_NOTIFY 1000
#define SSL_R_SSLV3_ALERT_UNEXPECTED_MESSAGE 1010
#define SSL_R_SSLV3_ALERT_BAD_RECORD_MAC 1020
diff --git a/src/include/openssl/stack.h b/src/include/openssl/stack.h
index 1a0347e..46f57a3 100644
--- a/src/include/openssl/stack.h
+++ b/src/include/openssl/stack.h
@@ -245,7 +245,7 @@
// are defined in a header.
#define BORINGSSL_DEFINE_STACK_OF_IMPL(name, ptrtype, constptrtype) \
- DECLARE_STACK_OF(name); \
+ DECLARE_STACK_OF(name) \
\
typedef int (*stack_##name##_cmp_func)(constptrtype *a, constptrtype *b); \
\
diff --git a/src/include/openssl/x509.h b/src/include/openssl/x509.h
index 7db9466..430ffc0 100644
--- a/src/include/openssl/x509.h
+++ b/src/include/openssl/x509.h
@@ -680,8 +680,9 @@
OPENSSL_EXPORT X509_REQ *X509_REQ_dup(X509_REQ *req);
OPENSSL_EXPORT X509_ALGOR *X509_ALGOR_dup(X509_ALGOR *xn);
OPENSSL_EXPORT int X509_ALGOR_set0(X509_ALGOR *alg, const ASN1_OBJECT *aobj, int ptype, void *pval);
-OPENSSL_EXPORT void X509_ALGOR_get0(ASN1_OBJECT **paobj, int *pptype, void **ppval,
- X509_ALGOR *algor);
+OPENSSL_EXPORT void X509_ALGOR_get0(const ASN1_OBJECT **paobj, int *pptype,
+ const void **ppval,
+ const X509_ALGOR *algor);
OPENSSL_EXPORT void X509_ALGOR_set_md(X509_ALGOR *alg, const EVP_MD *md);
OPENSSL_EXPORT int X509_ALGOR_cmp(const X509_ALGOR *a, const X509_ALGOR *b);
diff --git a/src/ssl/ssl_asn1.cc b/src/ssl/ssl_asn1.cc
index 7bcfdd7..eb7df5b 100644
--- a/src/ssl/ssl_asn1.cc
+++ b/src/ssl/ssl_asn1.cc
@@ -152,49 +152,49 @@
static const unsigned kVersion = 1;
-static const int kTimeTag =
+static const unsigned kTimeTag =
CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 1;
-static const int kTimeoutTag =
+static const unsigned kTimeoutTag =
CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 2;
-static const int kPeerTag =
+static const unsigned kPeerTag =
CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 3;
-static const int kSessionIDContextTag =
+static const unsigned kSessionIDContextTag =
CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 4;
-static const int kVerifyResultTag =
+static const unsigned kVerifyResultTag =
CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 5;
-static const int kHostNameTag =
+static const unsigned kHostNameTag =
CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 6;
-static const int kPSKIdentityTag =
+static const unsigned kPSKIdentityTag =
CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 8;
-static const int kTicketLifetimeHintTag =
+static const unsigned kTicketLifetimeHintTag =
CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 9;
-static const int kTicketTag =
+static const unsigned kTicketTag =
CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 10;
-static const int kPeerSHA256Tag =
+static const unsigned kPeerSHA256Tag =
CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 13;
-static const int kOriginalHandshakeHashTag =
+static const unsigned kOriginalHandshakeHashTag =
CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 14;
-static const int kSignedCertTimestampListTag =
+static const unsigned kSignedCertTimestampListTag =
CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 15;
-static const int kOCSPResponseTag =
+static const unsigned kOCSPResponseTag =
CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 16;
-static const int kExtendedMasterSecretTag =
+static const unsigned kExtendedMasterSecretTag =
CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 17;
-static const int kGroupIDTag =
+static const unsigned kGroupIDTag =
CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 18;
-static const int kCertChainTag =
+static const unsigned kCertChainTag =
CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 19;
-static const int kTicketAgeAddTag =
+static const unsigned kTicketAgeAddTag =
CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 21;
-static const int kIsServerTag =
+static const unsigned kIsServerTag =
CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 22;
-static const int kPeerSignatureAlgorithmTag =
+static const unsigned kPeerSignatureAlgorithmTag =
CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 23;
-static const int kTicketMaxEarlyDataTag =
+static const unsigned kTicketMaxEarlyDataTag =
CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 24;
-static const int kAuthTimeoutTag =
+static const unsigned kAuthTimeoutTag =
CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 25;
-static const int kEarlyALPNTag =
+static const unsigned kEarlyALPNTag =
CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 26;
static int SSL_SESSION_to_bytes_full(const SSL_SESSION *in, uint8_t **out_data,
diff --git a/src/ssl/ssl_privkey.cc b/src/ssl/ssl_privkey.cc
index e9990af..134ad56 100644
--- a/src/ssl/ssl_privkey.cc
+++ b/src/ssl/ssl_privkey.cc
@@ -193,33 +193,6 @@
return 1;
}
-static int legacy_sign_digest_supported(const SSL_SIGNATURE_ALGORITHM *alg) {
- return (alg->pkey_type == EVP_PKEY_EC || alg->pkey_type == EVP_PKEY_RSA) &&
- !alg->is_rsa_pss;
-}
-
-static enum ssl_private_key_result_t legacy_sign(
- SSL *ssl, uint8_t *out, size_t *out_len, size_t max_out, uint16_t sigalg,
- const uint8_t *in, size_t in_len) {
- // TODO(davidben): Remove support for |sign_digest|-only
- // |SSL_PRIVATE_KEY_METHOD|s.
- const SSL_SIGNATURE_ALGORITHM *alg = get_signature_algorithm(sigalg);
- if (alg == NULL || !legacy_sign_digest_supported(alg)) {
- OPENSSL_PUT_ERROR(SSL, SSL_R_UNSUPPORTED_PROTOCOL_FOR_CUSTOM_KEY);
- return ssl_private_key_failure;
- }
-
- const EVP_MD *md = alg->digest_func();
- uint8_t hash[EVP_MAX_MD_SIZE];
- unsigned hash_len;
- if (!EVP_Digest(in, in_len, hash, &hash_len, md, NULL)) {
- return ssl_private_key_failure;
- }
-
- return ssl->cert->key_method->sign_digest(ssl, out, out_len, max_out, md,
- hash, hash_len);
-}
-
enum ssl_private_key_result_t ssl_private_key_sign(
SSL_HANDSHAKE *hs, uint8_t *out, size_t *out_len, size_t max_out,
uint16_t sigalg, Span<const uint8_t> in) {
@@ -229,9 +202,8 @@
if (hs->pending_private_key_op) {
ret = ssl->cert->key_method->complete(ssl, out, out_len, max_out);
} else {
- ret = (ssl->cert->key_method->sign != NULL ? ssl->cert->key_method->sign
- : legacy_sign)(
- ssl, out, out_len, max_out, sigalg, in.data(), in.size());
+ ret = ssl->cert->key_method->sign(ssl, out, out_len, max_out, sigalg,
+ in.data(), in.size());
}
hs->pending_private_key_op = ret == ssl_private_key_retry;
return ret;
@@ -308,14 +280,6 @@
return false;
}
- // Newer algorithms require message-based private keys.
- // TODO(davidben): Remove this check when sign_digest is gone.
- if (ssl->cert->key_method != NULL &&
- ssl->cert->key_method->sign == NULL &&
- !legacy_sign_digest_supported(alg)) {
- return false;
- }
-
return true;
}
@@ -511,7 +475,6 @@
prefs, num_prefs);
}
-
int SSL_set_signing_algorithm_prefs(SSL *ssl, const uint16_t *prefs,
size_t num_prefs) {
return set_algorithm_prefs(&ssl->cert->sigalgs, &ssl->cert->num_sigalgs,
@@ -523,52 +486,3 @@
return set_algorithm_prefs(&ctx->verify_sigalgs, &ctx->num_verify_sigalgs,
prefs, num_prefs);
}
-
-int SSL_set_private_key_digest_prefs(SSL *ssl, const int *digest_nids,
- size_t num_digests) {
- OPENSSL_free(ssl->cert->sigalgs);
-
- static_assert(sizeof(int) >= 2 * sizeof(uint16_t),
- "sigalgs allocation may overflow");
-
- ssl->cert->num_sigalgs = 0;
- ssl->cert->sigalgs =
- (uint16_t *)OPENSSL_malloc(sizeof(uint16_t) * 2 * num_digests);
- if (ssl->cert->sigalgs == NULL) {
- OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
- return 0;
- }
-
- // Convert the digest list to a signature algorithms list.
- //
- // TODO(davidben): Replace this API with one that can express RSA-PSS, etc.
- for (size_t i = 0; i < num_digests; i++) {
- switch (digest_nids[i]) {
- case NID_sha1:
- ssl->cert->sigalgs[ssl->cert->num_sigalgs] = SSL_SIGN_RSA_PKCS1_SHA1;
- ssl->cert->sigalgs[ssl->cert->num_sigalgs + 1] = SSL_SIGN_ECDSA_SHA1;
- ssl->cert->num_sigalgs += 2;
- break;
- case NID_sha256:
- ssl->cert->sigalgs[ssl->cert->num_sigalgs] = SSL_SIGN_RSA_PKCS1_SHA256;
- ssl->cert->sigalgs[ssl->cert->num_sigalgs + 1] =
- SSL_SIGN_ECDSA_SECP256R1_SHA256;
- ssl->cert->num_sigalgs += 2;
- break;
- case NID_sha384:
- ssl->cert->sigalgs[ssl->cert->num_sigalgs] = SSL_SIGN_RSA_PKCS1_SHA384;
- ssl->cert->sigalgs[ssl->cert->num_sigalgs + 1] =
- SSL_SIGN_ECDSA_SECP384R1_SHA384;
- ssl->cert->num_sigalgs += 2;
- break;
- case NID_sha512:
- ssl->cert->sigalgs[ssl->cert->num_sigalgs] = SSL_SIGN_RSA_PKCS1_SHA512;
- ssl->cert->sigalgs[ssl->cert->num_sigalgs + 1] =
- SSL_SIGN_ECDSA_SECP521R1_SHA512;
- ssl->cert->num_sigalgs += 2;
- break;
- }
- }
-
- return 1;
-}
diff --git a/src/ssl/t1_lib.cc b/src/ssl/t1_lib.cc
index a7bfec4..8d03623 100644
--- a/src/ssl/t1_lib.cc
+++ b/src/ssl/t1_lib.cc
@@ -2147,6 +2147,7 @@
if (!CBS_get_u16(contents, &group_id) ||
!CBS_get_u16_length_prefixed(contents, &peer_key) ||
CBS_len(contents) != 0) {
+ OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
*out_alert = SSL_AD_DECODE_ERROR;
return false;
}
diff --git a/src/ssl/test/bssl_shim.cc b/src/ssl/test/bssl_shim.cc
index 7eca21d..8acabd7 100644
--- a/src/ssl/test/bssl_shim.cc
+++ b/src/ssl/test/bssl_shim.cc
@@ -433,10 +433,7 @@
}
static const SSL_PRIVATE_KEY_METHOD g_async_private_key_method = {
- nullptr /* type */,
- nullptr /* max_signature_len */,
AsyncPrivateKeySign,
- nullptr /* sign_digest */,
AsyncPrivateKeyDecrypt,
AsyncPrivateKeyComplete,
};
@@ -453,27 +450,6 @@
bssl::UniquePtr<EVP_PKEY> *out_pkey) {
const TestConfig *config = GetTestConfig(ssl);
- if (!config->digest_prefs.empty()) {
- bssl::UniquePtr<char> digest_prefs(
- OPENSSL_strdup(config->digest_prefs.c_str()));
- std::vector<int> digest_list;
-
- for (;;) {
- char *token =
- strtok(digest_list.empty() ? digest_prefs.get() : nullptr, ",");
- if (token == nullptr) {
- break;
- }
-
- digest_list.push_back(EVP_MD_type(EVP_get_digestbyname(token)));
- }
-
- if (!SSL_set_private_key_digest_prefs(ssl, digest_list.data(),
- digest_list.size())) {
- return false;
- }
- }
-
if (!config->signing_prefs.empty()) {
std::vector<uint16_t> u16s(config->signing_prefs.begin(),
config->signing_prefs.end());
diff --git a/src/ssl/test/runner/common.go b/src/ssl/test/runner/common.go
index 4564b0f..0216401 100644
--- a/src/ssl/test/runner/common.go
+++ b/src/ssl/test/runner/common.go
@@ -632,6 +632,10 @@
// ChangeCipherSpec messages.
SendExtraChangeCipherSpec int
+ // SendPostHandshakeChangeCipherSpec causes the implementation to send
+ // a ChangeCipherSpec record before every application data record.
+ SendPostHandshakeChangeCipherSpec bool
+
// SendUnencryptedFinished, if true, causes the Finished message to be
// send unencrypted before ChangeCipherSpec rather than after it.
SendUnencryptedFinished bool
diff --git a/src/ssl/test/runner/conn.go b/src/ssl/test/runner/conn.go
index 5359462..21f491a 100644
--- a/src/ssl/test/runner/conn.go
+++ b/src/ssl/test/runner/conn.go
@@ -39,7 +39,6 @@
vers uint16 // TLS version
haveVers bool // version has been negotiated
config *Config // configuration passed to constructor
- hadHelloRetryRequest bool
handshakeComplete bool
skipEarlyData bool // On a server, indicates that the client is sending early data that must be skipped over.
didResume bool // whether this connection was a session resumption
@@ -102,6 +101,8 @@
keyUpdateRequested bool
seenOneByteRecord bool
+ expectTLS13ChangeCipherSpec bool
+
tmp [16]byte
}
@@ -878,6 +879,47 @@
return typ, b, nil
}
+func (c *Conn) readTLS13ChangeCipherSpec() error {
+ if !c.expectTLS13ChangeCipherSpec {
+ panic("c.expectTLS13ChangeCipherSpec not set")
+ }
+
+ // Read the ChangeCipherSpec.
+ if c.rawInput == nil {
+ c.rawInput = c.in.newBlock()
+ }
+ b := c.rawInput
+ if err := b.readFromUntil(c.conn, 1); err != nil {
+ return c.in.setErrorLocked(fmt.Errorf("tls: error reading TLS 1.3 ChangeCipherSpec: %s", err))
+ }
+ if recordType(b.data[0]) == recordTypeAlert {
+ // If the client is sending an alert, allow the ChangeCipherSpec
+ // to be skipped. It may be rejecting a sufficiently malformed
+ // ServerHello that it can't parse out the version.
+ c.expectTLS13ChangeCipherSpec = false
+ return nil
+ }
+ if err := b.readFromUntil(c.conn, 6); err != nil {
+ return c.in.setErrorLocked(fmt.Errorf("tls: error reading TLS 1.3 ChangeCipherSpec: %s", err))
+ }
+
+ // Check they match that we expect.
+ expected := [6]byte{byte(recordTypeChangeCipherSpec), 3, 1, 0, 1, 1}
+ if isResumptionRecordVersionExperiment(c.wireVersion) {
+ expected[2] = 3
+ }
+ if !bytes.Equal(b.data[:6], expected[:]) {
+ return c.in.setErrorLocked(fmt.Errorf("tls: error invalid TLS 1.3 ChangeCipherSpec: %x", b.data[:6]))
+ }
+
+ // Discard the data.
+ b, c.rawInput = c.in.splitBlock(b, 6)
+ c.in.freeBlock(b)
+
+ c.expectTLS13ChangeCipherSpec = false
+ return nil
+}
+
// readRecord reads the next TLS record from the connection
// and updates the record layer state.
// c.in.Mutex <= L; c.input == nil.
@@ -898,6 +940,12 @@
break
}
+ if c.expectTLS13ChangeCipherSpec {
+ if err := c.readTLS13ChangeCipherSpec(); err != nil {
+ return err
+ }
+ }
+
Again:
typ, b, err := c.doReadRecord(want)
if err != nil {
@@ -952,14 +1000,12 @@
c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage))
break
}
- if !isResumptionExperiment(c.wireVersion) {
- if c.hand.Len() != 0 {
- c.in.setErrorLocked(errors.New("tls: buffered handshake messages on cipher change"))
- break
- }
- if err := c.in.changeCipherSpec(c.config); err != nil {
- c.in.setErrorLocked(c.sendAlert(err.(alert)))
- }
+ if c.hand.Len() != 0 {
+ c.in.setErrorLocked(errors.New("tls: buffered handshake messages on cipher change"))
+ break
+ }
+ if err := c.in.changeCipherSpec(c.config); err != nil {
+ c.in.setErrorLocked(c.sendAlert(err.(alert)))
}
case recordTypeApplicationData:
@@ -1089,6 +1135,12 @@
return 0, err
}
+ if typ == recordTypeApplicationData && c.config.Bugs.SendPostHandshakeChangeCipherSpec {
+ if _, err := c.doWriteRecord(recordTypeChangeCipherSpec, []byte{1}); err != nil {
+ return 0, err
+ }
+ }
+
return c.doWriteRecord(typ, data)
}
diff --git a/src/ssl/test/runner/fuzzer_mode.json b/src/ssl/test/runner/fuzzer_mode.json
index f2ecae0..fd029d3 100644
--- a/src/ssl/test/runner/fuzzer_mode.json
+++ b/src/ssl/test/runner/fuzzer_mode.json
@@ -35,17 +35,17 @@
"Resume-Server-*Binder*": "Fuzzer mode does not check binders.",
"SkipEarlyData*": "Trial decryption does not work with the NULL cipher.",
- "*-EarlyDataChannelID-OfferBoth-Server": "Trial decryption does not work with the NULL cipher.",
- "*-EarlyData-NonZeroRTTSession-Server": "Trial decryption does not work with the NULL cipher.",
- "*-EarlyData-SkipEndOfEarlyData": "Trial decryption does not work with the NULL cipher.",
- "*-EarlyData-ALPNMismatch-Server": "Trial decryption does not work with the NULL cipher.",
- "*-EarlyData-ALPNOmitted1-Client": "Trial decryption does not work with the NULL cipher.",
- "*-EarlyData-ALPNOmitted2-Client": "Trial decryption does not work with the NULL cipher.",
- "*-EarlyData-ALPNOmitted1-Server": "Trial decryption does not work with the NULL cipher.",
- "*-EarlyData-ALPNOmitted2-Server": "Trial decryption does not work with the NULL cipher.",
+ "EarlyDataChannelID-OfferBoth-Server-*": "Trial decryption does not work with the NULL cipher.",
+ "EarlyData-NonZeroRTTSession-Server-*": "Trial decryption does not work with the NULL cipher.",
+ "EarlyData-SkipEndOfEarlyData-*": "Trial decryption does not work with the NULL cipher.",
+ "EarlyData-ALPNMismatch-Server-*": "Trial decryption does not work with the NULL cipher.",
+ "EarlyData-ALPNOmitted1-Client-*": "Trial decryption does not work with the NULL cipher.",
+ "EarlyData-ALPNOmitted2-Client-*": "Trial decryption does not work with the NULL cipher.",
+ "EarlyData-ALPNOmitted1-Server-*": "Trial decryption does not work with the NULL cipher.",
+ "EarlyData-ALPNOmitted2-Server-*": "Trial decryption does not work with the NULL cipher.",
"*-EarlyData-RejectUnfinishedWrite-Client-*": "Trial decryption does not work with the NULL cipher.",
"EarlyData-Reject-Client-*": "Trial decryption does not work with the NULL cipher.",
- "*-EarlyData-RejectTicket-Client": "Trial decryption does not work with the NULL cipher.",
+ "EarlyData-RejectTicket-Client-*": "Trial decryption does not work with the NULL cipher.",
"Renegotiate-Client-BadExt*": "Fuzzer mode does not check renegotiation_info.",
diff --git a/src/ssl/test/runner/handshake_client.go b/src/ssl/test/runner/handshake_client.go
index 7c8dbb5..5f22ecd 100644
--- a/src/ssl/test/runner/handshake_client.go
+++ b/src/ssl/test/runner/handshake_client.go
@@ -485,12 +485,18 @@
c.vers = serverVersion
c.haveVers = true
+ if isDraft22(c.wireVersion) {
+ // The first server message must be followed by a ChangeCipherSpec.
+ c.expectTLS13ChangeCipherSpec = true
+ }
+
helloRetryRequest, haveHelloRetryRequest := msg.(*helloRetryRequestMsg)
var secondHelloBytes []byte
if haveHelloRetryRequest {
- c.hadHelloRetryRequest = true
if isDraft22(c.wireVersion) {
- if err := c.readRecord(recordTypeChangeCipherSpec); err != nil {
+ // Explicitly read the ChangeCipherSpec now; it should
+ // be attached to the first flight, not the second flight.
+ if err := c.readTLS13ChangeCipherSpec(); err != nil {
return err
}
}
@@ -718,6 +724,12 @@
func (hs *clientHandshakeState) doTLS13Handshake() error {
c := hs.c
+ if isResumptionExperiment(c.wireVersion) && !isDraft22(c.wireVersion) {
+ // Early versions of the middlebox hacks inserted
+ // ChangeCipherSpec differently on 0-RTT and 2-RTT handshakes.
+ c.expectTLS13ChangeCipherSpec = true
+ }
+
if isResumptionExperiment(c.wireVersion) && !bytes.Equal(hs.hello.sessionId, hs.serverHello.sessionId) {
return errors.New("tls: session IDs did not match.")
}
@@ -774,12 +786,6 @@
hs.finishedHash.addEntropy(zeroSecret)
}
- if isResumptionExperiment(c.wireVersion) && !c.hadHelloRetryRequest {
- if err := c.readRecord(recordTypeChangeCipherSpec); err != nil {
- return err
- }
- }
-
clientLabel := clientHandshakeTrafficLabel
serverLabel := serverHandshakeTrafficLabel
if isDraft21(c.wireVersion) {
diff --git a/src/ssl/test/runner/handshake_messages.go b/src/ssl/test/runner/handshake_messages.go
index d5e3951..93da121 100644
--- a/src/ssl/test/runner/handshake_messages.go
+++ b/src/ssl/test/runner/handshake_messages.go
@@ -1460,7 +1460,8 @@
extensions.addU16(2) // length
extensions.addU16(uint16(m.selectedGroup))
}
- if len(m.cookie) > 0 {
+ // m.cookie may be a non-nil empty slice for empty cookie tests.
+ if m.cookie != nil {
extensions.addU16(extensionCookie)
body := extensions.addU16LengthPrefixed()
body.addU16LengthPrefixed().addBytes(m.cookie)
diff --git a/src/ssl/test/runner/handshake_server.go b/src/ssl/test/runner/handshake_server.go
index 2513a00..4286911 100644
--- a/src/ssl/test/runner/handshake_server.go
+++ b/src/ssl/test/runner/handshake_server.go
@@ -358,6 +358,12 @@
c := hs.c
config := c.config
+ // We've read the ClientHello, so the next record in draft 22 must be
+ // preceded with ChangeCipherSpec.
+ if isDraft22(c.wireVersion) {
+ c.expectTLS13ChangeCipherSpec = true
+ }
+
hs.hello = &serverHelloMsg{
isDTLS: c.isDTLS,
vers: c.wireVersion,
@@ -691,12 +697,6 @@
earlyLabel = earlyTrafficLabelDraft21
}
- if isDraft22(c.wireVersion) {
- if err := c.readRecord(recordTypeChangeCipherSpec); err != nil {
- return err
- }
- }
-
earlyTrafficSecret := hs.finishedHash.deriveSecret(earlyLabel)
if err := c.useInTrafficSecret(c.wireVersion, hs.suite, earlyTrafficSecret); err != nil {
return err
@@ -996,11 +996,10 @@
}
}
}
-
- if isResumptionClientCCSExperiment(c.wireVersion) && !c.skipEarlyData && !encryptedExtensions.extensions.hasEarlyData {
- if err := c.readRecord(recordTypeChangeCipherSpec); err != nil {
- return err
- }
+ if isResumptionClientCCSExperiment(c.wireVersion) && !isDraft22(c.wireVersion) && !hs.clientHello.hasEarlyData {
+ // Early versions of the middlebox hacks inserted
+ // ChangeCipherSpec differently on 0-RTT and 2-RTT handshakes.
+ c.expectTLS13ChangeCipherSpec = true
}
// Switch input stream to handshake traffic keys.
diff --git a/src/ssl/test/runner/runner.go b/src/ssl/test/runner/runner.go
index 57bc20c..8700af2 100644
--- a/src/ssl/test/runner/runner.go
+++ b/src/ssl/test/runner/runner.go
@@ -2442,9 +2442,10 @@
SendLargeRecords: true,
},
},
- messageLen: maxPlaintext + 1,
- shouldFail: true,
- expectedError: ":DATA_LENGTH_TOO_LONG:",
+ messageLen: maxPlaintext + 1,
+ shouldFail: true,
+ expectedError: ":DATA_LENGTH_TOO_LONG:",
+ expectedLocalError: "remote error: record overflow",
},
{
protocol: dtls,
@@ -2454,9 +2455,64 @@
SendLargeRecords: true,
},
},
- messageLen: maxPlaintext + 1,
- shouldFail: true,
- expectedError: ":DATA_LENGTH_TOO_LONG:",
+ messageLen: maxPlaintext + 1,
+ shouldFail: true,
+ expectedError: ":DATA_LENGTH_TOO_LONG:",
+ expectedLocalError: "remote error: record overflow",
+ },
+ {
+ name: "LargePlaintext-TLS13-Padded-8192-8192",
+ config: Config{
+ MinVersion: VersionTLS13,
+ MaxVersion: VersionTLS13,
+ Bugs: ProtocolBugs{
+ RecordPadding: 8192,
+ SendLargeRecords: true,
+ },
+ },
+ messageLen: 8192,
+ },
+ {
+ name: "LargePlaintext-TLS13-Padded-8193-8192",
+ config: Config{
+ MinVersion: VersionTLS13,
+ MaxVersion: VersionTLS13,
+ Bugs: ProtocolBugs{
+ RecordPadding: 8193,
+ SendLargeRecords: true,
+ },
+ },
+ messageLen: 8192,
+ shouldFail: true,
+ expectedError: ":DATA_LENGTH_TOO_LONG:",
+ expectedLocalError: "remote error: record overflow",
+ },
+ {
+ name: "LargePlaintext-TLS13-Padded-16383-1",
+ config: Config{
+ MinVersion: VersionTLS13,
+ MaxVersion: VersionTLS13,
+ Bugs: ProtocolBugs{
+ RecordPadding: 1,
+ SendLargeRecords: true,
+ },
+ },
+ messageLen: 16383,
+ },
+ {
+ name: "LargePlaintext-TLS13-Padded-16384-1",
+ config: Config{
+ MinVersion: VersionTLS13,
+ MaxVersion: VersionTLS13,
+ Bugs: ProtocolBugs{
+ RecordPadding: 1,
+ SendLargeRecords: true,
+ },
+ },
+ messageLen: 16384,
+ shouldFail: true,
+ expectedError: ":DATA_LENGTH_TOO_LONG:",
+ expectedLocalError: "remote error: record overflow",
},
{
name: "LargeCiphertext",
@@ -8250,8 +8306,8 @@
expectedError: ":NO_COMMON_SIGNATURE_ALGORITHMS:",
})
- // Test that hash preferences are enforced. BoringSSL does not implement
- // MD5 signatures.
+ // Test that signature preferences are enforced. BoringSSL does not
+ // implement MD5 signatures.
testCases = append(testCases, testCase{
testType: serverTest,
name: "ClientAuth-Enforced",
@@ -8320,26 +8376,8 @@
expectedError: ":WRONG_SIGNATURE_TYPE:",
})
- // Test that the agreed upon digest respects the client preferences and
- // the server digests.
- testCases = append(testCases, testCase{
- name: "NoCommonAlgorithms-Digests",
- config: Config{
- MaxVersion: VersionTLS12,
- ClientAuth: RequireAnyClientCert,
- VerifySignatureAlgorithms: []signatureAlgorithm{
- signatureRSAPKCS1WithSHA512,
- signatureRSAPKCS1WithSHA1,
- },
- },
- flags: []string{
- "-cert-file", path.Join(*resourceDir, rsaCertificateFile),
- "-key-file", path.Join(*resourceDir, rsaKeyFile),
- "-digest-prefs", "SHA256",
- },
- shouldFail: true,
- expectedError: ":NO_COMMON_SIGNATURE_ALGORITHMS:",
- })
+ // Test that the negotiated signature algorithm respects the client and
+ // server preferences.
testCases = append(testCases, testCase{
name: "NoCommonAlgorithms",
config: Config{
@@ -8389,7 +8427,8 @@
flags: []string{
"-cert-file", path.Join(*resourceDir, rsaCertificateFile),
"-key-file", path.Join(*resourceDir, rsaKeyFile),
- "-digest-prefs", "SHA256,SHA1",
+ "-signing-prefs", strconv.Itoa(int(signatureRSAPKCS1WithSHA256)),
+ "-signing-prefs", strconv.Itoa(int(signatureRSAPKCS1WithSHA1)),
},
expectedPeerSignatureAlgorithm: signatureRSAPKCS1WithSHA256,
})
@@ -8405,7 +8444,9 @@
flags: []string{
"-cert-file", path.Join(*resourceDir, rsaCertificateFile),
"-key-file", path.Join(*resourceDir, rsaKeyFile),
- "-digest-prefs", "SHA512,SHA256,SHA1",
+ "-signing-prefs", strconv.Itoa(int(signatureRSAPKCS1WithSHA512)),
+ "-signing-prefs", strconv.Itoa(int(signatureRSAPKCS1WithSHA256)),
+ "-signing-prefs", strconv.Itoa(int(signatureRSAPKCS1WithSHA1)),
},
expectedPeerSignatureAlgorithm: signatureRSAPKCS1WithSHA1,
})
@@ -10793,13 +10834,38 @@
},
})
+ ret = append(ret, perMessageTest{
+ messageType: typeEndOfEarlyData,
+ test: testCase{
+ testType: serverTest,
+ name: "TLS13Draft22-EndOfEarlyData",
+ config: Config{
+ MaxVersion: VersionTLS13,
+ },
+ resumeConfig: &Config{
+ MaxVersion: VersionTLS13,
+ Bugs: ProtocolBugs{
+ SendEarlyData: [][]byte{{1, 2, 3, 4}},
+ ExpectEarlyDataAccepted: true,
+ },
+ },
+ tls13Variant: TLS13Draft22,
+ resumeSession: true,
+ flags: []string{"-enable-early-data"},
+ },
+ })
+
return ret
}
func addWrongMessageTypeTests() {
for _, t := range makePerMessageTests() {
t.test.name = "WrongMessageType-" + t.test.name
- t.test.config.Bugs.SendWrongMessageType = t.messageType
+ if t.test.resumeConfig != nil {
+ t.test.resumeConfig.Bugs.SendWrongMessageType = t.messageType
+ } else {
+ t.test.config.Bugs.SendWrongMessageType = t.messageType
+ }
t.test.shouldFail = true
t.test.expectedError = ":UNEXPECTED_MESSAGE:"
t.test.expectedLocalError = "remote error: unexpected message"
@@ -10834,7 +10900,11 @@
func addTrailingMessageDataTests() {
for _, t := range makePerMessageTests() {
t.test.name = "TrailingMessageData-" + t.test.name
- t.test.config.Bugs.SendTrailingMessageData = t.messageType
+ if t.test.resumeConfig != nil {
+ t.test.resumeConfig.Bugs.SendTrailingMessageData = t.messageType
+ } else {
+ t.test.config.Bugs.SendTrailingMessageData = t.messageType
+ }
t.test.shouldFail = true
t.test.expectedError = ":DECODE_ERROR:"
t.test.expectedLocalError = "remote error: error decoding message"
@@ -10858,59 +10928,6 @@
}
func addTLS13HandshakeTests() {
- testCases = append(testCases, testCase{
- testType: clientTest,
- name: "NegotiatePSKResumption-TLS13",
- config: Config{
- MaxVersion: VersionTLS13,
- Bugs: ProtocolBugs{
- NegotiatePSKResumption: true,
- },
- },
- resumeSession: true,
- shouldFail: true,
- expectedError: ":MISSING_KEY_SHARE:",
- })
-
- testCases = append(testCases, testCase{
- testType: clientTest,
- name: "MissingKeyShare-Client",
- config: Config{
- MaxVersion: VersionTLS13,
- Bugs: ProtocolBugs{
- MissingKeyShare: true,
- },
- },
- shouldFail: true,
- expectedError: ":MISSING_KEY_SHARE:",
- })
-
- testCases = append(testCases, testCase{
- testType: serverTest,
- name: "MissingKeyShare-Server",
- config: Config{
- MaxVersion: VersionTLS13,
- Bugs: ProtocolBugs{
- MissingKeyShare: true,
- },
- },
- shouldFail: true,
- expectedError: ":MISSING_KEY_SHARE:",
- })
-
- testCases = append(testCases, testCase{
- testType: serverTest,
- name: "DuplicateKeyShares",
- config: Config{
- MaxVersion: VersionTLS13,
- Bugs: ProtocolBugs{
- DuplicateKeyShares: true,
- },
- },
- shouldFail: true,
- expectedError: ":DUPLICATE_KEY_SHARE:",
- })
-
for _, version := range allVersions(tls) {
if version.version != VersionTLS13 {
continue
@@ -10919,6 +10936,63 @@
variant := version.tls13Variant
testCases = append(testCases, testCase{
+ testType: clientTest,
+ name: "NegotiatePSKResumption-" + name,
+ config: Config{
+ MaxVersion: VersionTLS13,
+ Bugs: ProtocolBugs{
+ NegotiatePSKResumption: true,
+ },
+ },
+ tls13Variant: variant,
+ resumeSession: true,
+ shouldFail: true,
+ expectedError: ":MISSING_KEY_SHARE:",
+ })
+
+ testCases = append(testCases, testCase{
+ testType: clientTest,
+ name: "MissingKeyShare-Client-" + name,
+ config: Config{
+ MaxVersion: VersionTLS13,
+ Bugs: ProtocolBugs{
+ MissingKeyShare: true,
+ },
+ },
+ tls13Variant: variant,
+ shouldFail: true,
+ expectedError: ":MISSING_KEY_SHARE:",
+ })
+
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: "MissingKeyShare-Server-" + name,
+ config: Config{
+ MaxVersion: VersionTLS13,
+ Bugs: ProtocolBugs{
+ MissingKeyShare: true,
+ },
+ },
+ tls13Variant: variant,
+ shouldFail: true,
+ expectedError: ":MISSING_KEY_SHARE:",
+ })
+
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: "DuplicateKeyShares-" + name,
+ config: Config{
+ MaxVersion: VersionTLS13,
+ Bugs: ProtocolBugs{
+ DuplicateKeyShares: true,
+ },
+ },
+ tls13Variant: variant,
+ shouldFail: true,
+ expectedError: ":DUPLICATE_KEY_SHARE:",
+ })
+
+ testCases = append(testCases, testCase{
testType: serverTest,
name: "SkipEarlyData-" + name,
config: Config{
@@ -11091,1266 +11165,1357 @@
"-expect-accept-early-data",
},
})
+
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: "SkipEarlyData-OmitEarlyDataExtension-" + name,
+ config: Config{
+ MaxVersion: VersionTLS13,
+ Bugs: ProtocolBugs{
+ SendFakeEarlyDataLength: 4,
+ OmitEarlyDataExtension: true,
+ },
+ },
+ tls13Variant: variant,
+ shouldFail: true,
+ expectedError: ":DECRYPTION_FAILED_OR_BAD_RECORD_MAC:",
+ })
+
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: "SkipEarlyData-TooMuchData-" + name,
+ config: Config{
+ MaxVersion: VersionTLS13,
+ Bugs: ProtocolBugs{
+ SendFakeEarlyDataLength: 16384 + 1,
+ },
+ },
+ tls13Variant: variant,
+ shouldFail: true,
+ expectedError: ":TOO_MUCH_SKIPPED_EARLY_DATA:",
+ })
+
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: "SkipEarlyData-Interleaved-" + name,
+ config: Config{
+ MaxVersion: VersionTLS13,
+ Bugs: ProtocolBugs{
+ SendFakeEarlyDataLength: 4,
+ InterleaveEarlyData: true,
+ },
+ },
+ tls13Variant: variant,
+ shouldFail: true,
+ expectedError: ":DECRYPTION_FAILED_OR_BAD_RECORD_MAC:",
+ })
+
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: "SkipEarlyData-EarlyDataInTLS12-" + name,
+ config: Config{
+ MaxVersion: VersionTLS13,
+ Bugs: ProtocolBugs{
+ SendFakeEarlyDataLength: 4,
+ },
+ },
+ tls13Variant: variant,
+ shouldFail: true,
+ expectedError: ":UNEXPECTED_RECORD:",
+ flags: []string{"-max-version", strconv.Itoa(VersionTLS12)},
+ })
+
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: "SkipEarlyData-HRR-" + name,
+ config: Config{
+ MaxVersion: VersionTLS13,
+ Bugs: ProtocolBugs{
+ SendFakeEarlyDataLength: 4,
+ },
+ DefaultCurves: []CurveID{},
+ },
+ tls13Variant: variant,
+ })
+
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: "SkipEarlyData-HRR-Interleaved-" + name,
+ config: Config{
+ MaxVersion: VersionTLS13,
+ Bugs: ProtocolBugs{
+ SendFakeEarlyDataLength: 4,
+ InterleaveEarlyData: true,
+ },
+ DefaultCurves: []CurveID{},
+ },
+ tls13Variant: variant,
+ shouldFail: true,
+ expectedError: ":UNEXPECTED_RECORD:",
+ })
+
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: "SkipEarlyData-HRR-TooMuchData-" + name,
+ config: Config{
+ MaxVersion: VersionTLS13,
+ Bugs: ProtocolBugs{
+ SendFakeEarlyDataLength: 16384 + 1,
+ },
+ DefaultCurves: []CurveID{},
+ },
+ tls13Variant: variant,
+ shouldFail: true,
+ expectedError: ":TOO_MUCH_SKIPPED_EARLY_DATA:",
+ })
+
+ // Test that skipping early data looking for cleartext correctly
+ // processes an alert record.
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: "SkipEarlyData-HRR-FatalAlert-" + name,
+ config: Config{
+ MaxVersion: VersionTLS13,
+ Bugs: ProtocolBugs{
+ SendEarlyAlert: true,
+ SendFakeEarlyDataLength: 4,
+ },
+ DefaultCurves: []CurveID{},
+ },
+ tls13Variant: variant,
+ shouldFail: true,
+ expectedError: ":SSLV3_ALERT_HANDSHAKE_FAILURE:",
+ })
+
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: "SkipEarlyData-SecondClientHelloEarlyData-" + name,
+ config: Config{
+ MaxVersion: VersionTLS13,
+ Bugs: ProtocolBugs{
+ SendEarlyDataOnSecondClientHello: true,
+ },
+ DefaultCurves: []CurveID{},
+ },
+ tls13Variant: variant,
+ shouldFail: true,
+ expectedLocalError: "remote error: bad record MAC",
+ })
+
+ testCases = append(testCases, testCase{
+ testType: clientTest,
+ name: "EmptyEncryptedExtensions-" + name,
+ config: Config{
+ MaxVersion: VersionTLS13,
+ Bugs: ProtocolBugs{
+ EmptyEncryptedExtensions: true,
+ },
+ },
+ tls13Variant: variant,
+ shouldFail: true,
+ expectedLocalError: "remote error: error decoding message",
+ })
+
+ testCases = append(testCases, testCase{
+ testType: clientTest,
+ name: "EncryptedExtensionsWithKeyShare-" + name,
+ config: Config{
+ MaxVersion: VersionTLS13,
+ Bugs: ProtocolBugs{
+ EncryptedExtensionsWithKeyShare: true,
+ },
+ },
+ tls13Variant: variant,
+ shouldFail: true,
+ expectedLocalError: "remote error: unsupported extension",
+ })
+
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: "SendHelloRetryRequest-" + name,
+ config: Config{
+ MaxVersion: VersionTLS13,
+ // Require a HelloRetryRequest for every curve.
+ DefaultCurves: []CurveID{},
+ },
+ tls13Variant: variant,
+ expectedCurveID: CurveX25519,
+ })
+
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: "SendHelloRetryRequest-2-" + name,
+ config: Config{
+ MaxVersion: VersionTLS13,
+ DefaultCurves: []CurveID{CurveP384},
+ },
+ tls13Variant: variant,
+ // Although the ClientHello did not predict our preferred curve,
+ // we always select it whether it is predicted or not.
+ expectedCurveID: CurveX25519,
+ })
+
+ testCases = append(testCases, testCase{
+ name: "UnknownCurve-HelloRetryRequest-" + name,
+ config: Config{
+ MaxVersion: VersionTLS13,
+ // P-384 requires HelloRetryRequest in BoringSSL.
+ CurvePreferences: []CurveID{CurveP384},
+ Bugs: ProtocolBugs{
+ SendHelloRetryRequestCurve: bogusCurve,
+ },
+ },
+ tls13Variant: variant,
+ shouldFail: true,
+ expectedError: ":WRONG_CURVE:",
+ })
+
+ if isDraft21(version.versionWire) {
+ testCases = append(testCases, testCase{
+ name: "HelloRetryRequest-CipherChange-" + name,
+ config: Config{
+ MaxVersion: VersionTLS13,
+ // P-384 requires HelloRetryRequest in BoringSSL.
+ CurvePreferences: []CurveID{CurveP384},
+ Bugs: ProtocolBugs{
+ SendCipherSuite: TLS_AES_128_GCM_SHA256,
+ SendHelloRetryRequestCipherSuite: TLS_CHACHA20_POLY1305_SHA256,
+ },
+ },
+ tls13Variant: variant,
+ shouldFail: true,
+ expectedError: ":WRONG_CIPHER_RETURNED:",
+ })
+
+ // Test that the client does not offer a PSK in the second ClientHello if the
+ // HelloRetryRequest is incompatible with it.
+ testCases = append(testCases, testCase{
+ testType: clientTest,
+ name: "HelloRetryRequest-NonResumableCipher-" + name,
+ config: Config{
+ MaxVersion: VersionTLS13,
+ CipherSuites: []uint16{
+ TLS_AES_128_GCM_SHA256,
+ },
+ },
+ resumeConfig: &Config{
+ MaxVersion: VersionTLS13,
+ // P-384 requires HelloRetryRequest in BoringSSL.
+ CurvePreferences: []CurveID{CurveP384},
+ Bugs: ProtocolBugs{
+ ExpectNoTLS13PSKAfterHRR: true,
+ },
+ CipherSuites: []uint16{
+ TLS_AES_256_GCM_SHA384,
+ },
+ },
+ tls13Variant: variant,
+ resumeSession: true,
+ expectResumeRejected: true,
+ })
+ }
+
+ testCases = append(testCases, testCase{
+ name: "DisabledCurve-HelloRetryRequest-" + name,
+ config: Config{
+ MaxVersion: VersionTLS13,
+ CurvePreferences: []CurveID{CurveP256},
+ Bugs: ProtocolBugs{
+ IgnorePeerCurvePreferences: true,
+ },
+ },
+ tls13Variant: variant,
+ flags: []string{"-p384-only"},
+ shouldFail: true,
+ expectedError: ":WRONG_CURVE:",
+ })
+
+ testCases = append(testCases, testCase{
+ name: "UnnecessaryHelloRetryRequest-" + name,
+ config: Config{
+ MaxVersion: VersionTLS13,
+ CurvePreferences: []CurveID{CurveX25519},
+ Bugs: ProtocolBugs{
+ SendHelloRetryRequestCurve: CurveX25519,
+ },
+ },
+ tls13Variant: variant,
+ shouldFail: true,
+ expectedError: ":WRONG_CURVE:",
+ })
+
+ testCases = append(testCases, testCase{
+ name: "SecondHelloRetryRequest-" + name,
+ config: Config{
+ MaxVersion: VersionTLS13,
+ // P-384 requires HelloRetryRequest in BoringSSL.
+ CurvePreferences: []CurveID{CurveP384},
+ Bugs: ProtocolBugs{
+ SecondHelloRetryRequest: true,
+ },
+ },
+ tls13Variant: variant,
+ shouldFail: true,
+ expectedError: ":UNEXPECTED_MESSAGE:",
+ })
+
+ testCases = append(testCases, testCase{
+ name: "HelloRetryRequest-Empty-" + name,
+ config: Config{
+ MaxVersion: VersionTLS13,
+ Bugs: ProtocolBugs{
+ AlwaysSendHelloRetryRequest: true,
+ },
+ },
+ tls13Variant: variant,
+ shouldFail: true,
+ expectedError: ":EMPTY_HELLO_RETRY_REQUEST:",
+ expectedLocalError: "remote error: illegal parameter",
+ })
+
+ testCases = append(testCases, testCase{
+ name: "HelloRetryRequest-DuplicateCurve-" + name,
+ config: Config{
+ MaxVersion: VersionTLS13,
+ // P-384 requires a HelloRetryRequest against BoringSSL's default
+ // configuration. Assert this ExpectMissingKeyShare.
+ CurvePreferences: []CurveID{CurveP384},
+ Bugs: ProtocolBugs{
+ ExpectMissingKeyShare: true,
+ DuplicateHelloRetryRequestExtensions: true,
+ },
+ },
+ tls13Variant: variant,
+ shouldFail: true,
+ expectedError: ":DUPLICATE_EXTENSION:",
+ expectedLocalError: "remote error: illegal parameter",
+ })
+
+ testCases = append(testCases, testCase{
+ name: "HelloRetryRequest-Cookie-" + name,
+ config: Config{
+ MaxVersion: VersionTLS13,
+ Bugs: ProtocolBugs{
+ SendHelloRetryRequestCookie: []byte("cookie"),
+ },
+ },
+ tls13Variant: variant,
+ })
+
+ testCases = append(testCases, testCase{
+ name: "HelloRetryRequest-DuplicateCookie-" + name,
+ config: Config{
+ MaxVersion: VersionTLS13,
+ Bugs: ProtocolBugs{
+ SendHelloRetryRequestCookie: []byte("cookie"),
+ DuplicateHelloRetryRequestExtensions: true,
+ },
+ },
+ tls13Variant: variant,
+ shouldFail: true,
+ expectedError: ":DUPLICATE_EXTENSION:",
+ expectedLocalError: "remote error: illegal parameter",
+ })
+
+ testCases = append(testCases, testCase{
+ name: "HelloRetryRequest-EmptyCookie-" + name,
+ config: Config{
+ MaxVersion: VersionTLS13,
+ Bugs: ProtocolBugs{
+ SendHelloRetryRequestCookie: []byte{},
+ },
+ },
+ tls13Variant: variant,
+ shouldFail: true,
+ expectedError: ":DECODE_ERROR:",
+ })
+
+ testCases = append(testCases, testCase{
+ name: "HelloRetryRequest-Cookie-Curve-" + name,
+ config: Config{
+ MaxVersion: VersionTLS13,
+ // P-384 requires HelloRetryRequest in BoringSSL.
+ CurvePreferences: []CurveID{CurveP384},
+ Bugs: ProtocolBugs{
+ SendHelloRetryRequestCookie: []byte("cookie"),
+ ExpectMissingKeyShare: true,
+ },
+ },
+ tls13Variant: variant,
+ })
+
+ testCases = append(testCases, testCase{
+ name: "HelloRetryRequest-Unknown-" + name,
+ config: Config{
+ MaxVersion: VersionTLS13,
+ Bugs: ProtocolBugs{
+ CustomHelloRetryRequestExtension: "extension",
+ },
+ },
+ tls13Variant: variant,
+ shouldFail: true,
+ expectedError: ":UNEXPECTED_EXTENSION:",
+ expectedLocalError: "remote error: unsupported extension",
+ })
+
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: "SecondClientHelloMissingKeyShare-" + name,
+ config: Config{
+ MaxVersion: VersionTLS13,
+ DefaultCurves: []CurveID{},
+ Bugs: ProtocolBugs{
+ SecondClientHelloMissingKeyShare: true,
+ },
+ },
+ tls13Variant: variant,
+ shouldFail: true,
+ expectedError: ":MISSING_KEY_SHARE:",
+ })
+
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: "SecondClientHelloWrongCurve-" + name,
+ config: Config{
+ MaxVersion: VersionTLS13,
+ DefaultCurves: []CurveID{},
+ Bugs: ProtocolBugs{
+ MisinterpretHelloRetryRequestCurve: CurveP521,
+ },
+ },
+ tls13Variant: variant,
+ shouldFail: true,
+ expectedError: ":WRONG_CURVE:",
+ })
+
+ testCases = append(testCases, testCase{
+ name: "HelloRetryRequestVersionMismatch-" + name,
+ config: Config{
+ MaxVersion: VersionTLS13,
+ // P-384 requires HelloRetryRequest in BoringSSL.
+ CurvePreferences: []CurveID{CurveP384},
+ Bugs: ProtocolBugs{
+ SendServerHelloVersion: 0x0305,
+ },
+ },
+ tls13Variant: variant,
+ shouldFail: true,
+ expectedError: ":WRONG_VERSION_NUMBER:",
+ })
+
+ testCases = append(testCases, testCase{
+ name: "HelloRetryRequestCurveMismatch-" + name,
+ config: Config{
+ MaxVersion: VersionTLS13,
+ // P-384 requires HelloRetryRequest in BoringSSL.
+ CurvePreferences: []CurveID{CurveP384},
+ Bugs: ProtocolBugs{
+ // Send P-384 (correct) in the HelloRetryRequest.
+ SendHelloRetryRequestCurve: CurveP384,
+ // But send P-256 in the ServerHello.
+ SendCurve: CurveP256,
+ },
+ },
+ tls13Variant: variant,
+ shouldFail: true,
+ expectedError: ":WRONG_CURVE:",
+ })
+
+ // Test the server selecting a curve that requires a HelloRetryRequest
+ // without sending it.
+ testCases = append(testCases, testCase{
+ name: "SkipHelloRetryRequest-" + name,
+ config: Config{
+ MaxVersion: VersionTLS13,
+ // P-384 requires HelloRetryRequest in BoringSSL.
+ CurvePreferences: []CurveID{CurveP384},
+ Bugs: ProtocolBugs{
+ SkipHelloRetryRequest: true,
+ },
+ },
+ tls13Variant: variant,
+ shouldFail: true,
+ expectedError: ":WRONG_CURVE:",
+ })
+
+ testCases = append(testCases, testCase{
+ name: "RequestContextInHandshake-" + name,
+ config: Config{
+ MaxVersion: VersionTLS13,
+ MinVersion: VersionTLS13,
+ ClientAuth: RequireAnyClientCert,
+ Bugs: ProtocolBugs{
+ SendRequestContext: []byte("request context"),
+ },
+ },
+ tls13Variant: variant,
+ flags: []string{
+ "-cert-file", path.Join(*resourceDir, rsaCertificateFile),
+ "-key-file", path.Join(*resourceDir, rsaKeyFile),
+ },
+ shouldFail: true,
+ expectedError: ":DECODE_ERROR:",
+ })
+
+ if isDraft21(version.versionWire) {
+ testCases = append(testCases, testCase{
+ name: "UnknownExtensionInCertificateRequest-" + name,
+ config: Config{
+ MaxVersion: VersionTLS13,
+ MinVersion: VersionTLS13,
+ ClientAuth: RequireAnyClientCert,
+ Bugs: ProtocolBugs{
+ SendCustomCertificateRequest: 0x1212,
+ },
+ },
+ tls13Variant: variant,
+ flags: []string{
+ "-cert-file", path.Join(*resourceDir, rsaCertificateFile),
+ "-key-file", path.Join(*resourceDir, rsaKeyFile),
+ },
+ })
+
+ testCases = append(testCases, testCase{
+ name: "MissingSignatureAlgorithmsInCertificateRequest-" + name,
+ config: Config{
+ MaxVersion: VersionTLS13,
+ MinVersion: VersionTLS13,
+ ClientAuth: RequireAnyClientCert,
+ Bugs: ProtocolBugs{
+ OmitCertificateRequestAlgorithms: true,
+ },
+ },
+ tls13Variant: variant,
+ flags: []string{
+ "-cert-file", path.Join(*resourceDir, rsaCertificateFile),
+ "-key-file", path.Join(*resourceDir, rsaKeyFile),
+ },
+ shouldFail: true,
+ expectedError: ":DECODE_ERROR:",
+ })
+ }
+
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: "TrailingKeyShareData-" + name,
+ config: Config{
+ MaxVersion: VersionTLS13,
+ Bugs: ProtocolBugs{
+ TrailingKeyShareData: true,
+ },
+ },
+ tls13Variant: variant,
+ shouldFail: true,
+ expectedError: ":DECODE_ERROR:",
+ })
+
+ testCases = append(testCases, testCase{
+ name: "AlwaysSelectPSKIdentity-" + name,
+ config: Config{
+ MaxVersion: VersionTLS13,
+ Bugs: ProtocolBugs{
+ AlwaysSelectPSKIdentity: true,
+ },
+ },
+ tls13Variant: variant,
+ shouldFail: true,
+ expectedError: ":UNEXPECTED_EXTENSION:",
+ })
+
+ testCases = append(testCases, testCase{
+ name: "InvalidPSKIdentity-" + name,
+ config: Config{
+ MaxVersion: VersionTLS13,
+ Bugs: ProtocolBugs{
+ SelectPSKIdentityOnResume: 1,
+ },
+ },
+ tls13Variant: variant,
+ resumeSession: true,
+ shouldFail: true,
+ expectedError: ":PSK_IDENTITY_NOT_FOUND:",
+ })
+
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: "ExtraPSKIdentity-" + name,
+ config: Config{
+ MaxVersion: VersionTLS13,
+ Bugs: ProtocolBugs{
+ ExtraPSKIdentity: true,
+ SendExtraPSKBinder: true,
+ },
+ },
+ tls13Variant: variant,
+ resumeSession: true,
+ })
+
+ // Test that unknown NewSessionTicket extensions are tolerated.
+ testCases = append(testCases, testCase{
+ name: "CustomTicketExtension-" + name,
+ config: Config{
+ MaxVersion: VersionTLS13,
+ Bugs: ProtocolBugs{
+ CustomTicketExtension: "1234",
+ },
+ },
+ tls13Variant: variant,
+ })
+ testCases = append(testCases, testCase{
+ testType: clientTest,
+ name: "EarlyData-RejectTicket-Client-" + name,
+ config: Config{
+ MaxVersion: VersionTLS13,
+ MaxEarlyDataSize: 16384,
+ Certificates: []Certificate{rsaCertificate},
+ },
+ resumeConfig: &Config{
+ MaxVersion: VersionTLS13,
+ MaxEarlyDataSize: 16384,
+ Certificates: []Certificate{ecdsaP256Certificate},
+ SessionTicketsDisabled: true,
+ },
+ tls13Variant: variant,
+ resumeSession: true,
+ expectResumeRejected: true,
+ flags: []string{
+ "-enable-early-data",
+ "-expect-ticket-supports-early-data",
+ "-expect-reject-early-data",
+ "-on-resume-shim-writes-first",
+ "-on-initial-expect-peer-cert-file", path.Join(*resourceDir, rsaCertificateFile),
+ "-on-resume-expect-peer-cert-file", path.Join(*resourceDir, rsaCertificateFile),
+ "-on-retry-expect-peer-cert-file", path.Join(*resourceDir, ecdsaP256CertificateFile),
+ // Session tickets are disabled, so the runner will not send a ticket.
+ "-on-retry-expect-no-session",
+ },
+ })
+
+ testCases = append(testCases, testCase{
+ testType: clientTest,
+ name: "EarlyData-HRR-Client-" + name,
+ config: Config{
+ MaxVersion: VersionTLS13,
+ MaxEarlyDataSize: 16384,
+ },
+ resumeConfig: &Config{
+ MaxVersion: VersionTLS13,
+ MaxEarlyDataSize: 16384,
+ Bugs: ProtocolBugs{
+ SendHelloRetryRequestCookie: []byte{1, 2, 3, 4},
+ },
+ },
+ tls13Variant: variant,
+ resumeSession: true,
+ flags: []string{
+ "-enable-early-data",
+ "-expect-ticket-supports-early-data",
+ "-expect-reject-early-data",
+ },
+ })
+
+ // The client must check the server does not send the early_data
+ // extension while rejecting the session.
+ testCases = append(testCases, testCase{
+ testType: clientTest,
+ name: "EarlyDataWithoutResume-Client-" + name,
+ config: Config{
+ MaxVersion: VersionTLS13,
+ MaxEarlyDataSize: 16384,
+ },
+ resumeConfig: &Config{
+ MaxVersion: VersionTLS13,
+ SessionTicketsDisabled: true,
+ Bugs: ProtocolBugs{
+ SendEarlyDataExtension: true,
+ },
+ },
+ tls13Variant: variant,
+ resumeSession: true,
+ flags: []string{
+ "-enable-early-data",
+ "-expect-ticket-supports-early-data",
+ },
+ shouldFail: true,
+ expectedError: ":UNEXPECTED_EXTENSION:",
+ })
+
+ // The client must fail with a dedicated error code if the server
+ // responds with TLS 1.2 when offering 0-RTT.
+ testCases = append(testCases, testCase{
+ testType: clientTest,
+ name: "EarlyDataVersionDowngrade-Client-" + name,
+ config: Config{
+ MaxVersion: VersionTLS13,
+ MaxEarlyDataSize: 16384,
+ },
+ resumeConfig: &Config{
+ MaxVersion: VersionTLS12,
+ },
+ tls13Variant: variant,
+ resumeSession: true,
+ flags: []string{
+ "-enable-early-data",
+ "-expect-ticket-supports-early-data",
+ },
+ shouldFail: true,
+ expectedError: ":WRONG_VERSION_ON_EARLY_DATA:",
+ })
+
+ // Test that the client rejects an (unsolicited) early_data extension if
+ // the server sent an HRR.
+ testCases = append(testCases, testCase{
+ testType: clientTest,
+ name: "ServerAcceptsEarlyDataOnHRR-Client-" + name,
+ config: Config{
+ MaxVersion: VersionTLS13,
+ MaxEarlyDataSize: 16384,
+ },
+ resumeConfig: &Config{
+ MaxVersion: VersionTLS13,
+ MaxEarlyDataSize: 16384,
+ Bugs: ProtocolBugs{
+ SendHelloRetryRequestCookie: []byte{1, 2, 3, 4},
+ SendEarlyDataExtension: true,
+ },
+ },
+ tls13Variant: variant,
+ resumeSession: true,
+ flags: []string{
+ "-enable-early-data",
+ "-expect-ticket-supports-early-data",
+ "-expect-reject-early-data",
+ },
+ shouldFail: true,
+ expectedError: ":UNEXPECTED_EXTENSION:",
+ })
+
+ if isDraft22(version.versionWire) {
+ testCases = append(testCases, testCase{
+ testType: clientTest,
+ name: "SkipChangeCipherSpec-Client-" + name,
+ config: Config{
+ MaxVersion: VersionTLS13,
+ Bugs: ProtocolBugs{
+ SkipChangeCipherSpec: true,
+ },
+ },
+ tls13Variant: variant,
+ })
+
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: "SkipChangeCipherSpec-Server-" + name,
+ config: Config{
+ MaxVersion: VersionTLS13,
+ Bugs: ProtocolBugs{
+ SkipChangeCipherSpec: true,
+ },
+ },
+ tls13Variant: variant,
+ })
+
+ testCases = append(testCases, testCase{
+ testType: clientTest,
+ name: "TooManyChangeCipherSpec-Client-" + name,
+ config: Config{
+ MaxVersion: VersionTLS13,
+ Bugs: ProtocolBugs{
+ SendExtraChangeCipherSpec: 33,
+ },
+ },
+ tls13Variant: variant,
+ shouldFail: true,
+ expectedError: ":TOO_MANY_EMPTY_FRAGMENTS:",
+ })
+
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: "TooManyChangeCipherSpec-Server-" + name,
+ config: Config{
+ MaxVersion: VersionTLS13,
+ Bugs: ProtocolBugs{
+ SendExtraChangeCipherSpec: 33,
+ },
+ },
+ tls13Variant: variant,
+ shouldFail: true,
+ expectedError: ":TOO_MANY_EMPTY_FRAGMENTS:",
+ })
+
+ testCases = append(testCases, testCase{
+ name: "SendPostHandshakeChangeCipherSpec-" + name,
+ config: Config{
+ MaxVersion: VersionTLS13,
+ Bugs: ProtocolBugs{
+ SendPostHandshakeChangeCipherSpec: true,
+ },
+ },
+ tls13Variant: variant,
+ shouldFail: true,
+ expectedError: ":UNEXPECTED_RECORD:",
+ expectedLocalError: "remote error: unexpected message",
+ })
+ }
+
+ fooString := "foo"
+ barString := "bar"
+
+ // Test that the client reports the correct ALPN after a 0-RTT reject
+ // that changed it.
+ testCases = append(testCases, testCase{
+ testType: clientTest,
+ name: "EarlyData-ALPNMismatch-Client-" + name,
+ config: Config{
+ MaxVersion: VersionTLS13,
+ MaxEarlyDataSize: 16384,
+ Bugs: ProtocolBugs{
+ ALPNProtocol: &fooString,
+ },
+ },
+ resumeConfig: &Config{
+ MaxVersion: VersionTLS13,
+ MaxEarlyDataSize: 16384,
+ Bugs: ProtocolBugs{
+ ALPNProtocol: &barString,
+ },
+ },
+ tls13Variant: variant,
+ resumeSession: true,
+ flags: []string{
+ "-advertise-alpn", "\x03foo\x03bar",
+ "-enable-early-data",
+ "-expect-ticket-supports-early-data",
+ "-expect-reject-early-data",
+ "-on-initial-expect-alpn", "foo",
+ "-on-resume-expect-alpn", "foo",
+ "-on-retry-expect-alpn", "bar",
+ },
+ })
+
+ // Test that the client reports the correct ALPN after a 0-RTT reject if
+ // ALPN was omitted from the first connection.
+ testCases = append(testCases, testCase{
+ testType: clientTest,
+ name: "EarlyData-ALPNOmitted1-Client-" + name,
+ config: Config{
+ MaxVersion: VersionTLS13,
+ MaxEarlyDataSize: 16384,
+ },
+ resumeConfig: &Config{
+ MaxVersion: VersionTLS13,
+ MaxEarlyDataSize: 16384,
+ NextProtos: []string{"foo"},
+ },
+ tls13Variant: variant,
+ resumeSession: true,
+ flags: []string{
+ "-advertise-alpn", "\x03foo\x03bar",
+ "-enable-early-data",
+ "-expect-ticket-supports-early-data",
+ "-expect-reject-early-data",
+ "-on-initial-expect-alpn", "",
+ "-on-resume-expect-alpn", "",
+ "-on-retry-expect-alpn", "foo",
+ "-on-resume-shim-writes-first",
+ },
+ })
+
+ // Test that the client reports the correct ALPN after a 0-RTT reject if
+ // ALPN was omitted from the second connection.
+ testCases = append(testCases, testCase{
+ testType: clientTest,
+ name: "EarlyData-ALPNOmitted2-Client-" + name,
+ config: Config{
+ MaxVersion: VersionTLS13,
+ MaxEarlyDataSize: 16384,
+ NextProtos: []string{"foo"},
+ },
+ resumeConfig: &Config{
+ MaxVersion: VersionTLS13,
+ MaxEarlyDataSize: 16384,
+ },
+ tls13Variant: variant,
+ resumeSession: true,
+ flags: []string{
+ "-advertise-alpn", "\x03foo\x03bar",
+ "-enable-early-data",
+ "-expect-ticket-supports-early-data",
+ "-expect-reject-early-data",
+ "-on-initial-expect-alpn", "foo",
+ "-on-resume-expect-alpn", "foo",
+ "-on-retry-expect-alpn", "",
+ "-on-resume-shim-writes-first",
+ },
+ })
+
+ // Test that the client enforces ALPN match on 0-RTT accept.
+ testCases = append(testCases, testCase{
+ testType: clientTest,
+ name: "EarlyData-BadALPNMismatch-Client-" + name,
+ config: Config{
+ MaxVersion: VersionTLS13,
+ MaxEarlyDataSize: 16384,
+ Bugs: ProtocolBugs{
+ ALPNProtocol: &fooString,
+ },
+ },
+ resumeConfig: &Config{
+ MaxVersion: VersionTLS13,
+ MaxEarlyDataSize: 16384,
+ Bugs: ProtocolBugs{
+ AlwaysAcceptEarlyData: true,
+ ALPNProtocol: &barString,
+ },
+ },
+ tls13Variant: variant,
+ resumeSession: true,
+ flags: []string{
+ "-advertise-alpn", "\x03foo\x03bar",
+ "-enable-early-data",
+ "-expect-ticket-supports-early-data",
+ "-on-initial-expect-alpn", "foo",
+ "-on-resume-expect-alpn", "foo",
+ "-on-retry-expect-alpn", "bar",
+ },
+ shouldFail: true,
+ expectedError: ":ALPN_MISMATCH_ON_EARLY_DATA:",
+ })
+
+ // Test that the client does not offer early data if it is incompatible
+ // with ALPN preferences.
+ testCases = append(testCases, testCase{
+ testType: clientTest,
+ name: "EarlyData-ALPNPreferenceChanged-" + name,
+ config: Config{
+ MaxVersion: VersionTLS13,
+ MaxEarlyDataSize: 16384,
+ NextProtos: []string{"foo", "bar"},
+ },
+ tls13Variant: variant,
+ resumeSession: true,
+ flags: []string{
+ "-enable-early-data",
+ "-expect-ticket-supports-early-data",
+ "-expect-no-offer-early-data",
+ "-on-initial-advertise-alpn", "\x03foo",
+ "-on-resume-advertise-alpn", "\x03bar",
+ "-on-initial-expect-alpn", "foo",
+ "-on-resume-expect-alpn", "bar",
+ },
+ })
+
+ // Test that the server correctly rejects 0-RTT when the previous
+ // session did not allow early data on resumption.
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: "EarlyData-NonZeroRTTSession-Server-" + name,
+ config: Config{
+ MaxVersion: VersionTLS13,
+ },
+ resumeConfig: &Config{
+ MaxVersion: VersionTLS13,
+ Bugs: ProtocolBugs{
+ SendEarlyData: [][]byte{{1, 2, 3, 4}},
+ ExpectEarlyDataAccepted: false,
+ },
+ },
+ tls13Variant: variant,
+ resumeSession: true,
+ flags: []string{
+ "-on-resume-enable-early-data",
+ "-expect-reject-early-data",
+ },
+ })
+
+ // Test that we reject early data where ALPN is omitted from the first
+ // connection.
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: "EarlyData-ALPNOmitted1-Server-" + name,
+ config: Config{
+ MaxVersion: VersionTLS13,
+ NextProtos: []string{},
+ },
+ resumeConfig: &Config{
+ MaxVersion: VersionTLS13,
+ NextProtos: []string{"foo"},
+ Bugs: ProtocolBugs{
+ SendEarlyData: [][]byte{{1, 2, 3, 4}},
+ ExpectEarlyDataAccepted: false,
+ },
+ },
+ tls13Variant: variant,
+ resumeSession: true,
+ flags: []string{
+ "-enable-early-data",
+ "-on-initial-select-alpn", "",
+ "-on-resume-select-alpn", "foo",
+ },
+ })
+
+ // Test that we reject early data where ALPN is omitted from the second
+ // connection.
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: "EarlyData-ALPNOmitted2-Server-" + name,
+ config: Config{
+ MaxVersion: VersionTLS13,
+ NextProtos: []string{"foo"},
+ },
+ resumeConfig: &Config{
+ MaxVersion: VersionTLS13,
+ NextProtos: []string{},
+ Bugs: ProtocolBugs{
+ SendEarlyData: [][]byte{{1, 2, 3, 4}},
+ ExpectEarlyDataAccepted: false,
+ },
+ },
+ tls13Variant: variant,
+ resumeSession: true,
+ flags: []string{
+ "-enable-early-data",
+ "-on-initial-select-alpn", "foo",
+ "-on-resume-select-alpn", "",
+ },
+ })
+
+ // Test that we reject early data with mismatched ALPN.
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: "EarlyData-ALPNMismatch-Server-" + name,
+ config: Config{
+ MaxVersion: VersionTLS13,
+ NextProtos: []string{"foo"},
+ },
+ resumeConfig: &Config{
+ MaxVersion: VersionTLS13,
+ NextProtos: []string{"bar"},
+ Bugs: ProtocolBugs{
+ SendEarlyData: [][]byte{{1, 2, 3, 4}},
+ ExpectEarlyDataAccepted: false,
+ },
+ },
+ tls13Variant: variant,
+ resumeSession: true,
+ flags: []string{
+ "-enable-early-data",
+ "-on-initial-select-alpn", "foo",
+ "-on-resume-select-alpn", "bar",
+ },
+ })
+
+ // Test that the client offering 0-RTT and Channel ID forbids the server
+ // from accepting both.
+ testCases = append(testCases, testCase{
+ testType: clientTest,
+ name: "EarlyDataChannelID-AcceptBoth-Client-" + name,
+ config: Config{
+ MaxVersion: VersionTLS13,
+ MaxEarlyDataSize: 16384,
+ RequestChannelID: true,
+ },
+ tls13Variant: variant,
+ resumeSession: true,
+ expectChannelID: true,
+ shouldFail: true,
+ expectedError: ":UNEXPECTED_EXTENSION_ON_EARLY_DATA:",
+ flags: []string{
+ "-enable-early-data",
+ "-expect-ticket-supports-early-data",
+ "-send-channel-id", path.Join(*resourceDir, channelIDKeyFile),
+ },
+ })
+
+ // Test that the client offering Channel ID and 0-RTT allows the server
+ // to decline 0-RTT.
+ testCases = append(testCases, testCase{
+ testType: clientTest,
+ name: "EarlyDataChannelID-AcceptChannelID-Client-" + name,
+ config: Config{
+ MaxVersion: VersionTLS13,
+ MaxEarlyDataSize: 16384,
+ RequestChannelID: true,
+ Bugs: ProtocolBugs{
+ AlwaysRejectEarlyData: true,
+ },
+ },
+ tls13Variant: variant,
+ resumeSession: true,
+ expectChannelID: true,
+ flags: []string{
+ "-enable-early-data",
+ "-expect-ticket-supports-early-data",
+ "-send-channel-id", path.Join(*resourceDir, channelIDKeyFile),
+ "-expect-reject-early-data",
+ },
+ })
+
+ // Test that the client offering Channel ID and 0-RTT allows the server
+ // to decline Channel ID.
+ testCases = append(testCases, testCase{
+ testType: clientTest,
+ name: "EarlyDataChannelID-AcceptEarlyData-Client-" + name,
+ config: Config{
+ MaxVersion: VersionTLS13,
+ MaxEarlyDataSize: 16384,
+ },
+ tls13Variant: variant,
+ resumeSession: true,
+ flags: []string{
+ "-enable-early-data",
+ "-expect-ticket-supports-early-data",
+ "-send-channel-id", path.Join(*resourceDir, channelIDKeyFile),
+ "-expect-accept-early-data",
+ },
+ })
+
+ // Test that the server supporting Channel ID and 0-RTT declines 0-RTT
+ // if it would negotiate Channel ID.
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: "EarlyDataChannelID-OfferBoth-Server-" + name,
+ config: Config{
+ MaxVersion: VersionTLS13,
+ ChannelID: channelIDKey,
+ Bugs: ProtocolBugs{
+ SendEarlyData: [][]byte{{1, 2, 3, 4}},
+ ExpectEarlyDataAccepted: false,
+ },
+ },
+ tls13Variant: variant,
+ resumeSession: true,
+ expectChannelID: true,
+ flags: []string{
+ "-enable-early-data",
+ "-expect-reject-early-data",
+ "-expect-channel-id",
+ base64.StdEncoding.EncodeToString(channelIDBytes),
+ },
+ })
+
+ // Test that the server supporting Channel ID and 0-RTT accepts 0-RTT
+ // if not offered Channel ID.
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: "EarlyDataChannelID-OfferEarlyData-Server-" + name,
+ config: Config{
+ MaxVersion: VersionTLS13,
+ Bugs: ProtocolBugs{
+ SendEarlyData: [][]byte{{1, 2, 3, 4}},
+ ExpectEarlyDataAccepted: true,
+ ExpectHalfRTTData: [][]byte{{254, 253, 252, 251}},
+ },
+ },
+ tls13Variant: variant,
+ resumeSession: true,
+ expectChannelID: false,
+ flags: []string{
+ "-enable-early-data",
+ "-expect-accept-early-data",
+ "-enable-channel-id",
+ },
+ })
+
+ // Test that the server rejects 0-RTT streams without end_of_early_data.
+ // The subsequent records should fail to decrypt.
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: "EarlyData-SkipEndOfEarlyData-" + name,
+ config: Config{
+ MaxVersion: VersionTLS13,
+ Bugs: ProtocolBugs{
+ SendEarlyData: [][]byte{{1, 2, 3, 4}},
+ ExpectEarlyDataAccepted: true,
+ SkipEndOfEarlyData: true,
+ },
+ },
+ tls13Variant: variant,
+ resumeSession: true,
+ flags: []string{"-enable-early-data"},
+ shouldFail: true,
+ expectedLocalError: "remote error: bad record MAC",
+ expectedError: ":BAD_DECRYPT:",
+ })
+
+ expectedError := ":UNEXPECTED_RECORD:"
+ if isDraft21(version.versionWire) {
+ // In draft-21 and up, early data is expected to be
+ // terminated by a handshake message, though we test
+ // with the wrong one.
+ expectedError = ":UNEXPECTED_MESSAGE:"
+ }
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: "EarlyData-UnexpectedHandshake-Server-" + name,
+ config: Config{
+ MaxVersion: VersionTLS13,
+ },
+ resumeConfig: &Config{
+ MaxVersion: VersionTLS13,
+ Bugs: ProtocolBugs{
+ SendEarlyData: [][]byte{{1, 2, 3, 4}},
+ SendStrayEarlyHandshake: true,
+ ExpectEarlyDataAccepted: true,
+ },
+ },
+ tls13Variant: variant,
+ resumeSession: true,
+ shouldFail: true,
+ expectedError: expectedError,
+ expectedLocalError: "remote error: unexpected message",
+ flags: []string{
+ "-enable-early-data",
+ },
+ })
+
+ // Test that the client reports TLS 1.3 as the version while sending
+ // early data.
+ testCases = append(testCases, testCase{
+ testType: clientTest,
+ name: "EarlyData-Client-VersionAPI-" + name,
+ config: Config{
+ MaxVersion: VersionTLS13,
+ MaxEarlyDataSize: 16384,
+ },
+ tls13Variant: variant,
+ resumeSession: true,
+ flags: []string{
+ "-enable-early-data",
+ "-expect-ticket-supports-early-data",
+ "-expect-accept-early-data",
+ "-expect-version", strconv.Itoa(VersionTLS13),
+ },
+ })
+
+ // Test that client and server both notice handshake errors after data
+ // has started flowing.
+ testCases = append(testCases, testCase{
+ testType: clientTest,
+ name: "EarlyData-Client-BadFinished-" + name,
+ config: Config{
+ MaxVersion: VersionTLS13,
+ MaxEarlyDataSize: 16384,
+ },
+ resumeConfig: &Config{
+ MaxVersion: VersionTLS13,
+ MaxEarlyDataSize: 16384,
+ Bugs: ProtocolBugs{
+ BadFinished: true,
+ },
+ },
+ tls13Variant: variant,
+ resumeSession: true,
+ flags: []string{
+ "-enable-early-data",
+ "-expect-ticket-supports-early-data",
+ "-expect-accept-early-data",
+ },
+ shouldFail: true,
+ expectedError: ":DIGEST_CHECK_FAILED:",
+ expectedLocalError: "remote error: error decrypting message",
+ })
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: "EarlyData-Server-BadFinished-" + name,
+ config: Config{
+ MaxVersion: VersionTLS13,
+ MaxEarlyDataSize: 16384,
+ },
+ resumeConfig: &Config{
+ MaxVersion: VersionTLS13,
+ MaxEarlyDataSize: 16384,
+ Bugs: ProtocolBugs{
+ SendEarlyData: [][]byte{{1, 2, 3, 4}},
+ ExpectEarlyDataAccepted: true,
+ ExpectHalfRTTData: [][]byte{{254, 253, 252, 251}},
+ BadFinished: true,
+ },
+ },
+ tls13Variant: variant,
+ resumeSession: true,
+ flags: []string{
+ "-enable-early-data",
+ "-expect-accept-early-data",
+ },
+ shouldFail: true,
+ expectedError: ":DIGEST_CHECK_FAILED:",
+ expectedLocalError: "remote error: error decrypting message",
+ })
+
+ if isDraft21(version.versionWire) {
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: "Server-NonEmptyEndOfEarlyData-" + name,
+ config: Config{
+ MaxVersion: VersionTLS13,
+ MaxEarlyDataSize: 16384,
+ },
+ resumeConfig: &Config{
+ MaxVersion: VersionTLS13,
+ MaxEarlyDataSize: 16384,
+ Bugs: ProtocolBugs{
+ SendEarlyData: [][]byte{{1, 2, 3, 4}},
+ ExpectEarlyDataAccepted: true,
+ NonEmptyEndOfEarlyData: true,
+ },
+ },
+ resumeSession: true,
+ flags: []string{
+ "-enable-early-data",
+ "-expect-ticket-supports-early-data",
+ "-expect-accept-early-data",
+ },
+ tls13Variant: variant,
+ shouldFail: true,
+ expectedError: ":DECODE_ERROR:",
+ })
+ }
+
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: "ServerSkipCertificateVerify-" + name,
+ config: Config{
+ MinVersion: VersionTLS13,
+ MaxVersion: VersionTLS13,
+ Certificates: []Certificate{rsaChainCertificate},
+ Bugs: ProtocolBugs{
+ SkipCertificateVerify: true,
+ },
+ },
+ tls13Variant: variant,
+ expectPeerCertificate: &rsaChainCertificate,
+ flags: []string{
+ "-cert-file", path.Join(*resourceDir, rsaChainCertificateFile),
+ "-key-file", path.Join(*resourceDir, rsaChainKeyFile),
+ "-require-any-client-certificate",
+ },
+ shouldFail: true,
+ expectedError: ":UNEXPECTED_MESSAGE:",
+ expectedLocalError: "remote error: unexpected message",
+ })
+ testCases = append(testCases, testCase{
+ testType: clientTest,
+ name: "ClientSkipCertificateVerify-" + name,
+ config: Config{
+ MinVersion: VersionTLS13,
+ MaxVersion: VersionTLS13,
+ Certificates: []Certificate{rsaChainCertificate},
+ Bugs: ProtocolBugs{
+ SkipCertificateVerify: true,
+ },
+ },
+ tls13Variant: variant,
+ expectPeerCertificate: &rsaChainCertificate,
+ flags: []string{
+ "-cert-file", path.Join(*resourceDir, rsaChainCertificateFile),
+ "-key-file", path.Join(*resourceDir, rsaChainKeyFile),
+ },
+ shouldFail: true,
+ expectedError: ":UNEXPECTED_MESSAGE:",
+ expectedLocalError: "remote error: unexpected message",
+ })
}
-
- testCases = append(testCases, testCase{
- testType: serverTest,
- name: "SkipEarlyData-OmitEarlyDataExtension",
- config: Config{
- MaxVersion: VersionTLS13,
- Bugs: ProtocolBugs{
- SendFakeEarlyDataLength: 4,
- OmitEarlyDataExtension: true,
- },
- },
- shouldFail: true,
- expectedError: ":DECRYPTION_FAILED_OR_BAD_RECORD_MAC:",
- })
-
- testCases = append(testCases, testCase{
- testType: serverTest,
- name: "SkipEarlyData-TooMuchData",
- config: Config{
- MaxVersion: VersionTLS13,
- Bugs: ProtocolBugs{
- SendFakeEarlyDataLength: 16384 + 1,
- },
- },
- shouldFail: true,
- expectedError: ":TOO_MUCH_SKIPPED_EARLY_DATA:",
- })
-
- testCases = append(testCases, testCase{
- testType: serverTest,
- name: "SkipEarlyData-Interleaved",
- config: Config{
- MaxVersion: VersionTLS13,
- Bugs: ProtocolBugs{
- SendFakeEarlyDataLength: 4,
- InterleaveEarlyData: true,
- },
- },
- shouldFail: true,
- expectedError: ":DECRYPTION_FAILED_OR_BAD_RECORD_MAC:",
- })
-
- testCases = append(testCases, testCase{
- testType: serverTest,
- name: "SkipEarlyData-EarlyDataInTLS12",
- config: Config{
- MaxVersion: VersionTLS13,
- Bugs: ProtocolBugs{
- SendFakeEarlyDataLength: 4,
- },
- },
- shouldFail: true,
- expectedError: ":UNEXPECTED_RECORD:",
- flags: []string{"-max-version", strconv.Itoa(VersionTLS12)},
- })
-
- testCases = append(testCases, testCase{
- testType: serverTest,
- name: "SkipEarlyData-HRR",
- config: Config{
- MaxVersion: VersionTLS13,
- Bugs: ProtocolBugs{
- SendFakeEarlyDataLength: 4,
- },
- DefaultCurves: []CurveID{},
- },
- })
-
- testCases = append(testCases, testCase{
- testType: serverTest,
- name: "SkipEarlyData-HRR-Interleaved",
- config: Config{
- MaxVersion: VersionTLS13,
- Bugs: ProtocolBugs{
- SendFakeEarlyDataLength: 4,
- InterleaveEarlyData: true,
- },
- DefaultCurves: []CurveID{},
- },
- shouldFail: true,
- expectedError: ":UNEXPECTED_RECORD:",
- })
-
- testCases = append(testCases, testCase{
- testType: serverTest,
- name: "SkipEarlyData-HRR-TooMuchData",
- config: Config{
- MaxVersion: VersionTLS13,
- Bugs: ProtocolBugs{
- SendFakeEarlyDataLength: 16384 + 1,
- },
- DefaultCurves: []CurveID{},
- },
- shouldFail: true,
- expectedError: ":TOO_MUCH_SKIPPED_EARLY_DATA:",
- })
-
- // Test that skipping early data looking for cleartext correctly
- // processes an alert record.
- testCases = append(testCases, testCase{
- testType: serverTest,
- name: "SkipEarlyData-HRR-FatalAlert",
- config: Config{
- MaxVersion: VersionTLS13,
- Bugs: ProtocolBugs{
- SendEarlyAlert: true,
- SendFakeEarlyDataLength: 4,
- },
- DefaultCurves: []CurveID{},
- },
- shouldFail: true,
- expectedError: ":SSLV3_ALERT_HANDSHAKE_FAILURE:",
- })
-
- testCases = append(testCases, testCase{
- testType: serverTest,
- name: "SkipEarlyData-SecondClientHelloEarlyData",
- config: Config{
- MaxVersion: VersionTLS13,
- Bugs: ProtocolBugs{
- SendEarlyDataOnSecondClientHello: true,
- },
- DefaultCurves: []CurveID{},
- },
- shouldFail: true,
- expectedLocalError: "remote error: bad record MAC",
- })
-
- testCases = append(testCases, testCase{
- testType: clientTest,
- name: "EmptyEncryptedExtensions",
- config: Config{
- MaxVersion: VersionTLS13,
- Bugs: ProtocolBugs{
- EmptyEncryptedExtensions: true,
- },
- },
- shouldFail: true,
- expectedLocalError: "remote error: error decoding message",
- })
-
- testCases = append(testCases, testCase{
- testType: clientTest,
- name: "EncryptedExtensionsWithKeyShare",
- config: Config{
- MaxVersion: VersionTLS13,
- Bugs: ProtocolBugs{
- EncryptedExtensionsWithKeyShare: true,
- },
- },
- shouldFail: true,
- expectedLocalError: "remote error: unsupported extension",
- })
-
- testCases = append(testCases, testCase{
- testType: serverTest,
- name: "SendHelloRetryRequest",
- config: Config{
- MaxVersion: VersionTLS13,
- // Require a HelloRetryRequest for every curve.
- DefaultCurves: []CurveID{},
- },
- expectedCurveID: CurveX25519,
- })
-
- testCases = append(testCases, testCase{
- testType: serverTest,
- name: "SendHelloRetryRequest-2",
- config: Config{
- MaxVersion: VersionTLS13,
- DefaultCurves: []CurveID{CurveP384},
- },
- // Although the ClientHello did not predict our preferred curve,
- // we always select it whether it is predicted or not.
- expectedCurveID: CurveX25519,
- })
-
- testCases = append(testCases, testCase{
- name: "UnknownCurve-HelloRetryRequest",
- config: Config{
- MaxVersion: VersionTLS13,
- // P-384 requires HelloRetryRequest in BoringSSL.
- CurvePreferences: []CurveID{CurveP384},
- Bugs: ProtocolBugs{
- SendHelloRetryRequestCurve: bogusCurve,
- },
- },
- shouldFail: true,
- expectedError: ":WRONG_CURVE:",
- })
-
- testCases = append(testCases, testCase{
- name: "TLS13Draft21-HelloRetryRequest-CipherChange",
- config: Config{
- MaxVersion: VersionTLS13,
- // P-384 requires HelloRetryRequest in BoringSSL.
- CurvePreferences: []CurveID{CurveP384},
- Bugs: ProtocolBugs{
- SendCipherSuite: TLS_AES_128_GCM_SHA256,
- SendHelloRetryRequestCipherSuite: TLS_CHACHA20_POLY1305_SHA256,
- },
- },
- tls13Variant: TLS13Draft21,
- shouldFail: true,
- expectedError: ":WRONG_CIPHER_RETURNED:",
- })
-
- // Test that the client does not offer a PSK in the second ClientHello if the
- // HelloRetryRequest is incompatible with it.
- testCases = append(testCases, testCase{
- testType: clientTest,
- name: "TLS13Draft21-HelloRetryRequest-NonResumableCipher",
- config: Config{
- MaxVersion: VersionTLS13,
- CipherSuites: []uint16{
- TLS_AES_128_GCM_SHA256,
- },
- },
- resumeConfig: &Config{
- MaxVersion: VersionTLS13,
- // P-384 requires HelloRetryRequest in BoringSSL.
- CurvePreferences: []CurveID{CurveP384},
- Bugs: ProtocolBugs{
- ExpectNoTLS13PSKAfterHRR: true,
- },
- CipherSuites: []uint16{
- TLS_AES_256_GCM_SHA384,
- },
- },
- tls13Variant: TLS13Draft21,
- resumeSession: true,
- expectResumeRejected: true,
- })
-
- testCases = append(testCases, testCase{
- name: "DisabledCurve-HelloRetryRequest",
- config: Config{
- MaxVersion: VersionTLS13,
- CurvePreferences: []CurveID{CurveP256},
- Bugs: ProtocolBugs{
- IgnorePeerCurvePreferences: true,
- },
- },
- flags: []string{"-p384-only"},
- shouldFail: true,
- expectedError: ":WRONG_CURVE:",
- })
-
- testCases = append(testCases, testCase{
- name: "UnnecessaryHelloRetryRequest",
- config: Config{
- MaxVersion: VersionTLS13,
- CurvePreferences: []CurveID{CurveX25519},
- Bugs: ProtocolBugs{
- SendHelloRetryRequestCurve: CurveX25519,
- },
- },
- shouldFail: true,
- expectedError: ":WRONG_CURVE:",
- })
-
- testCases = append(testCases, testCase{
- name: "SecondHelloRetryRequest",
- config: Config{
- MaxVersion: VersionTLS13,
- // P-384 requires HelloRetryRequest in BoringSSL.
- CurvePreferences: []CurveID{CurveP384},
- Bugs: ProtocolBugs{
- SecondHelloRetryRequest: true,
- },
- },
- shouldFail: true,
- expectedError: ":UNEXPECTED_MESSAGE:",
- })
-
- testCases = append(testCases, testCase{
- name: "HelloRetryRequest-Empty",
- config: Config{
- MaxVersion: VersionTLS13,
- Bugs: ProtocolBugs{
- AlwaysSendHelloRetryRequest: true,
- },
- },
- shouldFail: true,
- expectedError: ":DECODE_ERROR:",
- })
-
- testCases = append(testCases, testCase{
- name: "HelloRetryRequest-DuplicateCurve",
- config: Config{
- MaxVersion: VersionTLS13,
- // P-384 requires a HelloRetryRequest against BoringSSL's default
- // configuration. Assert this ExpectMissingKeyShare.
- CurvePreferences: []CurveID{CurveP384},
- Bugs: ProtocolBugs{
- ExpectMissingKeyShare: true,
- DuplicateHelloRetryRequestExtensions: true,
- },
- },
- shouldFail: true,
- expectedError: ":DUPLICATE_EXTENSION:",
- expectedLocalError: "remote error: illegal parameter",
- })
-
- testCases = append(testCases, testCase{
- name: "HelloRetryRequest-Cookie",
- config: Config{
- MaxVersion: VersionTLS13,
- Bugs: ProtocolBugs{
- SendHelloRetryRequestCookie: []byte("cookie"),
- },
- },
- })
-
- testCases = append(testCases, testCase{
- name: "HelloRetryRequest-DuplicateCookie",
- config: Config{
- MaxVersion: VersionTLS13,
- Bugs: ProtocolBugs{
- SendHelloRetryRequestCookie: []byte("cookie"),
- DuplicateHelloRetryRequestExtensions: true,
- },
- },
- shouldFail: true,
- expectedError: ":DUPLICATE_EXTENSION:",
- expectedLocalError: "remote error: illegal parameter",
- })
-
- testCases = append(testCases, testCase{
- name: "HelloRetryRequest-EmptyCookie",
- config: Config{
- MaxVersion: VersionTLS13,
- Bugs: ProtocolBugs{
- SendHelloRetryRequestCookie: []byte{},
- },
- },
- shouldFail: true,
- expectedError: ":DECODE_ERROR:",
- })
-
- testCases = append(testCases, testCase{
- name: "HelloRetryRequest-Cookie-Curve",
- config: Config{
- MaxVersion: VersionTLS13,
- // P-384 requires HelloRetryRequest in BoringSSL.
- CurvePreferences: []CurveID{CurveP384},
- Bugs: ProtocolBugs{
- SendHelloRetryRequestCookie: []byte("cookie"),
- ExpectMissingKeyShare: true,
- },
- },
- })
-
- testCases = append(testCases, testCase{
- name: "HelloRetryRequest-Unknown",
- config: Config{
- MaxVersion: VersionTLS13,
- Bugs: ProtocolBugs{
- CustomHelloRetryRequestExtension: "extension",
- },
- },
- shouldFail: true,
- expectedError: ":UNEXPECTED_EXTENSION:",
- expectedLocalError: "remote error: unsupported extension",
- })
-
- testCases = append(testCases, testCase{
- testType: serverTest,
- name: "SecondClientHelloMissingKeyShare",
- config: Config{
- MaxVersion: VersionTLS13,
- DefaultCurves: []CurveID{},
- Bugs: ProtocolBugs{
- SecondClientHelloMissingKeyShare: true,
- },
- },
- shouldFail: true,
- expectedError: ":MISSING_KEY_SHARE:",
- })
-
- testCases = append(testCases, testCase{
- testType: serverTest,
- name: "SecondClientHelloWrongCurve",
- config: Config{
- MaxVersion: VersionTLS13,
- DefaultCurves: []CurveID{},
- Bugs: ProtocolBugs{
- MisinterpretHelloRetryRequestCurve: CurveP521,
- },
- },
- shouldFail: true,
- expectedError: ":WRONG_CURVE:",
- })
-
- testCases = append(testCases, testCase{
- name: "HelloRetryRequestVersionMismatch",
- config: Config{
- MaxVersion: VersionTLS13,
- // P-384 requires HelloRetryRequest in BoringSSL.
- CurvePreferences: []CurveID{CurveP384},
- Bugs: ProtocolBugs{
- SendServerHelloVersion: 0x0305,
- },
- },
- shouldFail: true,
- expectedError: ":WRONG_VERSION_NUMBER:",
- })
-
- testCases = append(testCases, testCase{
- name: "HelloRetryRequestCurveMismatch",
- config: Config{
- MaxVersion: VersionTLS13,
- // P-384 requires HelloRetryRequest in BoringSSL.
- CurvePreferences: []CurveID{CurveP384},
- Bugs: ProtocolBugs{
- // Send P-384 (correct) in the HelloRetryRequest.
- SendHelloRetryRequestCurve: CurveP384,
- // But send P-256 in the ServerHello.
- SendCurve: CurveP256,
- },
- },
- shouldFail: true,
- expectedError: ":WRONG_CURVE:",
- })
-
- // Test the server selecting a curve that requires a HelloRetryRequest
- // without sending it.
- testCases = append(testCases, testCase{
- name: "SkipHelloRetryRequest",
- config: Config{
- MaxVersion: VersionTLS13,
- // P-384 requires HelloRetryRequest in BoringSSL.
- CurvePreferences: []CurveID{CurveP384},
- Bugs: ProtocolBugs{
- SkipHelloRetryRequest: true,
- },
- },
- shouldFail: true,
- expectedError: ":WRONG_CURVE:",
- })
-
- testCases = append(testCases, testCase{
- name: "TLS13-RequestContextInHandshake",
- config: Config{
- MaxVersion: VersionTLS13,
- MinVersion: VersionTLS13,
- ClientAuth: RequireAnyClientCert,
- Bugs: ProtocolBugs{
- SendRequestContext: []byte("request context"),
- },
- },
- flags: []string{
- "-cert-file", path.Join(*resourceDir, rsaCertificateFile),
- "-key-file", path.Join(*resourceDir, rsaKeyFile),
- },
- shouldFail: true,
- expectedError: ":DECODE_ERROR:",
- })
-
- testCases = append(testCases, testCase{
- name: "TLS13-UnknownInCertificateRequest",
- config: Config{
- MaxVersion: VersionTLS13,
- MinVersion: VersionTLS13,
- ClientAuth: RequireAnyClientCert,
- Bugs: ProtocolBugs{
- SendCustomCertificateRequest: 0x1212,
- },
- },
- tls13Variant: TLS13Draft21,
- flags: []string{
- "-cert-file", path.Join(*resourceDir, rsaCertificateFile),
- "-key-file", path.Join(*resourceDir, rsaKeyFile),
- },
- })
-
- testCases = append(testCases, testCase{
- name: "TLS13-MissingSignatureAlgorithmsInCertificateRequest",
- config: Config{
- MaxVersion: VersionTLS13,
- MinVersion: VersionTLS13,
- ClientAuth: RequireAnyClientCert,
- Bugs: ProtocolBugs{
- OmitCertificateRequestAlgorithms: true,
- },
- },
- tls13Variant: TLS13Draft21,
- flags: []string{
- "-cert-file", path.Join(*resourceDir, rsaCertificateFile),
- "-key-file", path.Join(*resourceDir, rsaKeyFile),
- },
- shouldFail: true,
- expectedError: ":DECODE_ERROR:",
- })
-
- testCases = append(testCases, testCase{
- testType: serverTest,
- name: "TLS13-TrailingKeyShareData",
- config: Config{
- MaxVersion: VersionTLS13,
- Bugs: ProtocolBugs{
- TrailingKeyShareData: true,
- },
- },
- shouldFail: true,
- expectedError: ":DECODE_ERROR:",
- })
-
- testCases = append(testCases, testCase{
- name: "TLS13-AlwaysSelectPSKIdentity",
- config: Config{
- MaxVersion: VersionTLS13,
- Bugs: ProtocolBugs{
- AlwaysSelectPSKIdentity: true,
- },
- },
- shouldFail: true,
- expectedError: ":UNEXPECTED_EXTENSION:",
- })
-
- testCases = append(testCases, testCase{
- name: "TLS13-InvalidPSKIdentity",
- config: Config{
- MaxVersion: VersionTLS13,
- Bugs: ProtocolBugs{
- SelectPSKIdentityOnResume: 1,
- },
- },
- resumeSession: true,
- shouldFail: true,
- expectedError: ":PSK_IDENTITY_NOT_FOUND:",
- })
-
- testCases = append(testCases, testCase{
- testType: serverTest,
- name: "TLS13-ExtraPSKIdentity",
- config: Config{
- MaxVersion: VersionTLS13,
- Bugs: ProtocolBugs{
- ExtraPSKIdentity: true,
- SendExtraPSKBinder: true,
- },
- },
- resumeSession: true,
- })
-
- // Test that unknown NewSessionTicket extensions are tolerated.
- testCases = append(testCases, testCase{
- name: "TLS13-CustomTicketExtension",
- config: Config{
- MaxVersion: VersionTLS13,
- Bugs: ProtocolBugs{
- CustomTicketExtension: "1234",
- },
- },
- })
- testCases = append(testCases, testCase{
- testType: clientTest,
- name: "TLS13-EarlyData-RejectTicket-Client",
- config: Config{
- MaxVersion: VersionTLS13,
- MaxEarlyDataSize: 16384,
- Certificates: []Certificate{rsaCertificate},
- },
- resumeConfig: &Config{
- MaxVersion: VersionTLS13,
- MaxEarlyDataSize: 16384,
- Certificates: []Certificate{ecdsaP256Certificate},
- SessionTicketsDisabled: true,
- },
- resumeSession: true,
- expectResumeRejected: true,
- flags: []string{
- "-enable-early-data",
- "-expect-ticket-supports-early-data",
- "-expect-reject-early-data",
- "-on-resume-shim-writes-first",
- "-on-initial-expect-peer-cert-file", path.Join(*resourceDir, rsaCertificateFile),
- "-on-resume-expect-peer-cert-file", path.Join(*resourceDir, rsaCertificateFile),
- "-on-retry-expect-peer-cert-file", path.Join(*resourceDir, ecdsaP256CertificateFile),
- // Session tickets are disabled, so the runner will not send a ticket.
- "-on-retry-expect-no-session",
- },
- })
-
- testCases = append(testCases, testCase{
- testType: clientTest,
- name: "TLS13-EarlyData-HRR-Client",
- config: Config{
- MaxVersion: VersionTLS13,
- MaxEarlyDataSize: 16384,
- },
- resumeConfig: &Config{
- MaxVersion: VersionTLS13,
- MaxEarlyDataSize: 16384,
- Bugs: ProtocolBugs{
- SendHelloRetryRequestCookie: []byte{1, 2, 3, 4},
- },
- },
- resumeSession: true,
- flags: []string{
- "-enable-early-data",
- "-expect-ticket-supports-early-data",
- "-expect-reject-early-data",
- },
- })
-
- // The client must check the server does not send the early_data
- // extension while rejecting the session.
- testCases = append(testCases, testCase{
- testType: clientTest,
- name: "TLS13-EarlyDataWithoutResume-Client",
- config: Config{
- MaxVersion: VersionTLS13,
- MaxEarlyDataSize: 16384,
- },
- resumeConfig: &Config{
- MaxVersion: VersionTLS13,
- SessionTicketsDisabled: true,
- Bugs: ProtocolBugs{
- SendEarlyDataExtension: true,
- },
- },
- resumeSession: true,
- flags: []string{
- "-enable-early-data",
- "-expect-ticket-supports-early-data",
- },
- shouldFail: true,
- expectedError: ":UNEXPECTED_EXTENSION:",
- })
-
- // The client must fail with a dedicated error code if the server
- // responds with TLS 1.2 when offering 0-RTT.
- testCases = append(testCases, testCase{
- testType: clientTest,
- name: "TLS13-EarlyDataVersionDowngrade-Client",
- config: Config{
- MaxVersion: VersionTLS13,
- MaxEarlyDataSize: 16384,
- },
- resumeConfig: &Config{
- MaxVersion: VersionTLS12,
- },
- resumeSession: true,
- flags: []string{
- "-enable-early-data",
- "-expect-ticket-supports-early-data",
- },
- shouldFail: true,
- expectedError: ":WRONG_VERSION_ON_EARLY_DATA:",
- })
-
- // Test that the client rejects an (unsolicited) early_data extension if
- // the server sent an HRR.
- testCases = append(testCases, testCase{
- testType: clientTest,
- name: "TLS13-ServerAcceptsEarlyDataOnHRR-Client",
- config: Config{
- MaxVersion: VersionTLS13,
- MaxEarlyDataSize: 16384,
- },
- resumeConfig: &Config{
- MaxVersion: VersionTLS13,
- MaxEarlyDataSize: 16384,
- Bugs: ProtocolBugs{
- SendHelloRetryRequestCookie: []byte{1, 2, 3, 4},
- SendEarlyDataExtension: true,
- },
- },
- resumeSession: true,
- flags: []string{
- "-enable-early-data",
- "-expect-ticket-supports-early-data",
- "-expect-reject-early-data",
- },
- shouldFail: true,
- expectedError: ":UNEXPECTED_EXTENSION:",
- })
-
- testCases = append(testCases, testCase{
- testType: clientTest,
- name: "TLS13Draft22-SkipChangeCipherSpec-Client",
- config: Config{
- MaxVersion: VersionTLS13,
- Bugs: ProtocolBugs{
- SkipChangeCipherSpec: true,
- },
- },
- tls13Variant: TLS13Draft22,
- })
-
- testCases = append(testCases, testCase{
- testType: serverTest,
- name: "TLS13Draft22-SkipChangeCipherSpec-Server",
- config: Config{
- MaxVersion: VersionTLS13,
- Bugs: ProtocolBugs{
- SkipChangeCipherSpec: true,
- },
- },
- tls13Variant: TLS13Draft22,
- })
-
- testCases = append(testCases, testCase{
- testType: clientTest,
- name: "TLS13Draft22-TooManyChangeCipherSpec-Client",
- config: Config{
- MaxVersion: VersionTLS13,
- Bugs: ProtocolBugs{
- SendExtraChangeCipherSpec: 33,
- },
- },
- tls13Variant: TLS13Draft22,
- shouldFail: true,
- expectedError: ":TOO_MANY_EMPTY_FRAGMENTS:",
- })
-
- testCases = append(testCases, testCase{
- testType: serverTest,
- name: "TLS13Draft22-TooManyChangeCipherSpec-Server",
- config: Config{
- MaxVersion: VersionTLS13,
- Bugs: ProtocolBugs{
- SendExtraChangeCipherSpec: 33,
- },
- },
- tls13Variant: TLS13Draft22,
- shouldFail: true,
- expectedError: ":TOO_MANY_EMPTY_FRAGMENTS:",
- })
-
- fooString := "foo"
- barString := "bar"
-
- // Test that the client reports the correct ALPN after a 0-RTT reject
- // that changed it.
- testCases = append(testCases, testCase{
- testType: clientTest,
- name: "TLS13-EarlyData-ALPNMismatch-Client",
- config: Config{
- MaxVersion: VersionTLS13,
- MaxEarlyDataSize: 16384,
- Bugs: ProtocolBugs{
- ALPNProtocol: &fooString,
- },
- },
- resumeConfig: &Config{
- MaxVersion: VersionTLS13,
- MaxEarlyDataSize: 16384,
- Bugs: ProtocolBugs{
- ALPNProtocol: &barString,
- },
- },
- resumeSession: true,
- flags: []string{
- "-advertise-alpn", "\x03foo\x03bar",
- "-enable-early-data",
- "-expect-ticket-supports-early-data",
- "-expect-reject-early-data",
- "-on-initial-expect-alpn", "foo",
- "-on-resume-expect-alpn", "foo",
- "-on-retry-expect-alpn", "bar",
- },
- })
-
- // Test that the client reports the correct ALPN after a 0-RTT reject if
- // ALPN was omitted from the first connection.
- testCases = append(testCases, testCase{
- testType: clientTest,
- name: "TLS13-EarlyData-ALPNOmitted1-Client",
- config: Config{
- MaxVersion: VersionTLS13,
- MaxEarlyDataSize: 16384,
- },
- resumeConfig: &Config{
- MaxVersion: VersionTLS13,
- MaxEarlyDataSize: 16384,
- NextProtos: []string{"foo"},
- },
- resumeSession: true,
- flags: []string{
- "-advertise-alpn", "\x03foo\x03bar",
- "-enable-early-data",
- "-expect-ticket-supports-early-data",
- "-expect-reject-early-data",
- "-on-initial-expect-alpn", "",
- "-on-resume-expect-alpn", "",
- "-on-retry-expect-alpn", "foo",
- "-on-resume-shim-writes-first",
- },
- })
-
- // Test that the client reports the correct ALPN after a 0-RTT reject if
- // ALPN was omitted from the second connection.
- testCases = append(testCases, testCase{
- testType: clientTest,
- name: "TLS13-EarlyData-ALPNOmitted2-Client",
- config: Config{
- MaxVersion: VersionTLS13,
- MaxEarlyDataSize: 16384,
- NextProtos: []string{"foo"},
- },
- resumeConfig: &Config{
- MaxVersion: VersionTLS13,
- MaxEarlyDataSize: 16384,
- },
- resumeSession: true,
- flags: []string{
- "-advertise-alpn", "\x03foo\x03bar",
- "-enable-early-data",
- "-expect-ticket-supports-early-data",
- "-expect-reject-early-data",
- "-on-initial-expect-alpn", "foo",
- "-on-resume-expect-alpn", "foo",
- "-on-retry-expect-alpn", "",
- "-on-resume-shim-writes-first",
- },
- })
-
- // Test that the client enforces ALPN match on 0-RTT accept.
- testCases = append(testCases, testCase{
- testType: clientTest,
- name: "TLS13-EarlyData-BadALPNMismatch-Client",
- config: Config{
- MaxVersion: VersionTLS13,
- MaxEarlyDataSize: 16384,
- Bugs: ProtocolBugs{
- ALPNProtocol: &fooString,
- },
- },
- resumeConfig: &Config{
- MaxVersion: VersionTLS13,
- MaxEarlyDataSize: 16384,
- Bugs: ProtocolBugs{
- AlwaysAcceptEarlyData: true,
- ALPNProtocol: &barString,
- },
- },
- resumeSession: true,
- flags: []string{
- "-advertise-alpn", "\x03foo\x03bar",
- "-enable-early-data",
- "-expect-ticket-supports-early-data",
- "-on-initial-expect-alpn", "foo",
- "-on-resume-expect-alpn", "foo",
- "-on-retry-expect-alpn", "bar",
- },
- shouldFail: true,
- expectedError: ":ALPN_MISMATCH_ON_EARLY_DATA:",
- })
-
- // Test that the client does not offer early data if it is incompatible
- // with ALPN preferences.
- testCases = append(testCases, testCase{
- testType: clientTest,
- name: "TLS13-EarlyData-ALPNPreferenceChanged",
- config: Config{
- MaxVersion: VersionTLS13,
- MaxEarlyDataSize: 16384,
- NextProtos: []string{"foo", "bar"},
- },
- resumeSession: true,
- flags: []string{
- "-enable-early-data",
- "-expect-ticket-supports-early-data",
- "-expect-no-offer-early-data",
- "-on-initial-advertise-alpn", "\x03foo",
- "-on-resume-advertise-alpn", "\x03bar",
- "-on-initial-expect-alpn", "foo",
- "-on-resume-expect-alpn", "bar",
- },
- })
-
- // Test that the server correctly rejects 0-RTT when the previous
- // session did not allow early data on resumption.
- testCases = append(testCases, testCase{
- testType: serverTest,
- name: "TLS13-EarlyData-NonZeroRTTSession-Server",
- config: Config{
- MaxVersion: VersionTLS13,
- },
- resumeConfig: &Config{
- MaxVersion: VersionTLS13,
- Bugs: ProtocolBugs{
- SendEarlyData: [][]byte{{1, 2, 3, 4}},
- ExpectEarlyDataAccepted: false,
- },
- },
- resumeSession: true,
- flags: []string{
- "-on-resume-enable-early-data",
- "-expect-reject-early-data",
- },
- })
-
- // Test that we reject early data where ALPN is omitted from the first
- // connection.
- testCases = append(testCases, testCase{
- testType: serverTest,
- name: "TLS13-EarlyData-ALPNOmitted1-Server",
- config: Config{
- MaxVersion: VersionTLS13,
- NextProtos: []string{},
- },
- resumeConfig: &Config{
- MaxVersion: VersionTLS13,
- NextProtos: []string{"foo"},
- Bugs: ProtocolBugs{
- SendEarlyData: [][]byte{{1, 2, 3, 4}},
- ExpectEarlyDataAccepted: false,
- },
- },
- resumeSession: true,
- flags: []string{
- "-enable-early-data",
- "-on-initial-select-alpn", "",
- "-on-resume-select-alpn", "foo",
- },
- })
-
- // Test that we reject early data where ALPN is omitted from the second
- // connection.
- testCases = append(testCases, testCase{
- testType: serverTest,
- name: "TLS13-EarlyData-ALPNOmitted2-Server",
- config: Config{
- MaxVersion: VersionTLS13,
- NextProtos: []string{"foo"},
- },
- resumeConfig: &Config{
- MaxVersion: VersionTLS13,
- NextProtos: []string{},
- Bugs: ProtocolBugs{
- SendEarlyData: [][]byte{{1, 2, 3, 4}},
- ExpectEarlyDataAccepted: false,
- },
- },
- resumeSession: true,
- flags: []string{
- "-enable-early-data",
- "-on-initial-select-alpn", "foo",
- "-on-resume-select-alpn", "",
- },
- })
-
- // Test that we reject early data with mismatched ALPN.
- testCases = append(testCases, testCase{
- testType: serverTest,
- name: "TLS13-EarlyData-ALPNMismatch-Server",
- config: Config{
- MaxVersion: VersionTLS13,
- NextProtos: []string{"foo"},
- },
- resumeConfig: &Config{
- MaxVersion: VersionTLS13,
- NextProtos: []string{"bar"},
- Bugs: ProtocolBugs{
- SendEarlyData: [][]byte{{1, 2, 3, 4}},
- ExpectEarlyDataAccepted: false,
- },
- },
- resumeSession: true,
- flags: []string{
- "-enable-early-data",
- "-on-initial-select-alpn", "foo",
- "-on-resume-select-alpn", "bar",
- },
- })
-
- // Test that the client offering 0-RTT and Channel ID forbids the server
- // from accepting both.
- testCases = append(testCases, testCase{
- testType: clientTest,
- name: "TLS13-EarlyDataChannelID-AcceptBoth-Client",
- config: Config{
- MaxVersion: VersionTLS13,
- MaxEarlyDataSize: 16384,
- RequestChannelID: true,
- },
- resumeSession: true,
- expectChannelID: true,
- shouldFail: true,
- expectedError: ":UNEXPECTED_EXTENSION_ON_EARLY_DATA:",
- flags: []string{
- "-enable-early-data",
- "-expect-ticket-supports-early-data",
- "-send-channel-id", path.Join(*resourceDir, channelIDKeyFile),
- },
- })
-
- // Test that the client offering Channel ID and 0-RTT allows the server
- // to decline 0-RTT.
- testCases = append(testCases, testCase{
- testType: clientTest,
- name: "TLS13-EarlyDataChannelID-AcceptChannelID-Client",
- config: Config{
- MaxVersion: VersionTLS13,
- MaxEarlyDataSize: 16384,
- RequestChannelID: true,
- Bugs: ProtocolBugs{
- AlwaysRejectEarlyData: true,
- },
- },
- resumeSession: true,
- expectChannelID: true,
- flags: []string{
- "-enable-early-data",
- "-expect-ticket-supports-early-data",
- "-send-channel-id", path.Join(*resourceDir, channelIDKeyFile),
- "-expect-reject-early-data",
- },
- })
-
- // Test that the client offering Channel ID and 0-RTT allows the server
- // to decline Channel ID.
- testCases = append(testCases, testCase{
- testType: clientTest,
- name: "TLS13-EarlyDataChannelID-AcceptEarlyData-Client",
- config: Config{
- MaxVersion: VersionTLS13,
- MaxEarlyDataSize: 16384,
- },
- resumeSession: true,
- flags: []string{
- "-enable-early-data",
- "-expect-ticket-supports-early-data",
- "-send-channel-id", path.Join(*resourceDir, channelIDKeyFile),
- "-expect-accept-early-data",
- },
- })
-
- // Test that the server supporting Channel ID and 0-RTT declines 0-RTT
- // if it would negotiate Channel ID.
- testCases = append(testCases, testCase{
- testType: serverTest,
- name: "TLS13-EarlyDataChannelID-OfferBoth-Server",
- config: Config{
- MaxVersion: VersionTLS13,
- ChannelID: channelIDKey,
- Bugs: ProtocolBugs{
- SendEarlyData: [][]byte{{1, 2, 3, 4}},
- ExpectEarlyDataAccepted: false,
- },
- },
- resumeSession: true,
- expectChannelID: true,
- flags: []string{
- "-enable-early-data",
- "-expect-reject-early-data",
- "-expect-channel-id",
- base64.StdEncoding.EncodeToString(channelIDBytes),
- },
- })
-
- // Test that the server supporting Channel ID and 0-RTT accepts 0-RTT
- // if not offered Channel ID.
- testCases = append(testCases, testCase{
- testType: serverTest,
- name: "TLS13-EarlyDataChannelID-OfferEarlyData-Server",
- config: Config{
- MaxVersion: VersionTLS13,
- Bugs: ProtocolBugs{
- SendEarlyData: [][]byte{{1, 2, 3, 4}},
- ExpectEarlyDataAccepted: true,
- ExpectHalfRTTData: [][]byte{{254, 253, 252, 251}},
- },
- },
- resumeSession: true,
- expectChannelID: false,
- flags: []string{
- "-enable-early-data",
- "-expect-accept-early-data",
- "-enable-channel-id",
- },
- })
-
- // Test that the server rejects 0-RTT streams without end_of_early_data.
- // The subsequent records should fail to decrypt.
- testCases = append(testCases, testCase{
- testType: serverTest,
- name: "TLS13-EarlyData-SkipEndOfEarlyData",
- config: Config{
- MaxVersion: VersionTLS13,
- Bugs: ProtocolBugs{
- SendEarlyData: [][]byte{{1, 2, 3, 4}},
- ExpectEarlyDataAccepted: true,
- SkipEndOfEarlyData: true,
- },
- },
- resumeSession: true,
- flags: []string{"-enable-early-data"},
- shouldFail: true,
- expectedLocalError: "remote error: bad record MAC",
- expectedError: ":BAD_DECRYPT:",
- })
-
- testCases = append(testCases, testCase{
- testType: serverTest,
- name: "TLS13-EarlyData-UnexpectedHandshake-Server",
- config: Config{
- MaxVersion: VersionTLS13,
- },
- resumeConfig: &Config{
- MaxVersion: VersionTLS13,
- Bugs: ProtocolBugs{
- SendEarlyData: [][]byte{{1, 2, 3, 4}},
- SendStrayEarlyHandshake: true,
- ExpectEarlyDataAccepted: true,
- },
- },
- resumeSession: true,
- shouldFail: true,
- expectedError: ":UNEXPECTED_RECORD:",
- expectedLocalError: "remote error: unexpected message",
- flags: []string{
- "-enable-early-data",
- },
- })
-
- // Test that the client reports TLS 1.3 as the version while sending
- // early data.
- testCases = append(testCases, testCase{
- testType: clientTest,
- name: "TLS13-EarlyData-Client-VersionAPI",
- config: Config{
- MaxVersion: VersionTLS13,
- MaxEarlyDataSize: 16384,
- },
- resumeSession: true,
- flags: []string{
- "-enable-early-data",
- "-expect-ticket-supports-early-data",
- "-expect-accept-early-data",
- "-expect-version", strconv.Itoa(VersionTLS13),
- },
- })
-
- // Test that client and server both notice handshake errors after data
- // has started flowing.
- testCases = append(testCases, testCase{
- testType: clientTest,
- name: "TLS13-EarlyData-Client-BadFinished",
- config: Config{
- MaxVersion: VersionTLS13,
- MaxEarlyDataSize: 16384,
- },
- resumeConfig: &Config{
- MaxVersion: VersionTLS13,
- MaxEarlyDataSize: 16384,
- Bugs: ProtocolBugs{
- BadFinished: true,
- },
- },
- resumeSession: true,
- flags: []string{
- "-enable-early-data",
- "-expect-ticket-supports-early-data",
- "-expect-accept-early-data",
- },
- shouldFail: true,
- expectedError: ":DIGEST_CHECK_FAILED:",
- expectedLocalError: "remote error: error decrypting message",
- })
- testCases = append(testCases, testCase{
- testType: serverTest,
- name: "TLS13-EarlyData-Server-BadFinished",
- config: Config{
- MaxVersion: VersionTLS13,
- MaxEarlyDataSize: 16384,
- },
- resumeConfig: &Config{
- MaxVersion: VersionTLS13,
- MaxEarlyDataSize: 16384,
- Bugs: ProtocolBugs{
- SendEarlyData: [][]byte{{1, 2, 3, 4}},
- ExpectEarlyDataAccepted: true,
- ExpectHalfRTTData: [][]byte{{254, 253, 252, 251}},
- BadFinished: true,
- },
- },
- resumeSession: true,
- flags: []string{
- "-enable-early-data",
- "-expect-accept-early-data",
- },
- shouldFail: true,
- expectedError: ":DIGEST_CHECK_FAILED:",
- expectedLocalError: "remote error: error decrypting message",
- })
-
- testCases = append(testCases, testCase{
- testType: serverTest,
- name: "TLS13Draft21-Server-NonEmptyEndOfEarlyData",
- config: Config{
- MaxVersion: VersionTLS13,
- MaxEarlyDataSize: 16384,
- },
- resumeConfig: &Config{
- MaxVersion: VersionTLS13,
- MaxEarlyDataSize: 16384,
- Bugs: ProtocolBugs{
- SendEarlyData: [][]byte{{1, 2, 3, 4}},
- ExpectEarlyDataAccepted: true,
- NonEmptyEndOfEarlyData: true,
- },
- },
- resumeSession: true,
- flags: []string{
- "-enable-early-data",
- "-expect-ticket-supports-early-data",
- "-expect-accept-early-data",
- },
- tls13Variant: TLS13Draft21,
- shouldFail: true,
- expectedError: ":DECODE_ERROR:",
- })
-
- testCases = append(testCases, testCase{
- testType: serverTest,
- name: "TLS13-ServerSkipCertificateVerify",
- config: Config{
- MinVersion: VersionTLS13,
- MaxVersion: VersionTLS13,
- Certificates: []Certificate{rsaChainCertificate},
- Bugs: ProtocolBugs{
- SkipCertificateVerify: true,
- },
- },
- expectPeerCertificate: &rsaChainCertificate,
- flags: []string{
- "-cert-file", path.Join(*resourceDir, rsaChainCertificateFile),
- "-key-file", path.Join(*resourceDir, rsaChainKeyFile),
- "-require-any-client-certificate",
- },
- shouldFail: true,
- expectedError: ":UNEXPECTED_MESSAGE:",
- expectedLocalError: "remote error: unexpected message",
- })
- testCases = append(testCases, testCase{
- testType: clientTest,
- name: "TLS13-ClientSkipCertificateVerify",
- config: Config{
- MinVersion: VersionTLS13,
- MaxVersion: VersionTLS13,
- Certificates: []Certificate{rsaChainCertificate},
- Bugs: ProtocolBugs{
- SkipCertificateVerify: true,
- },
- },
- expectPeerCertificate: &rsaChainCertificate,
- flags: []string{
- "-cert-file", path.Join(*resourceDir, rsaChainCertificateFile),
- "-key-file", path.Join(*resourceDir, rsaChainKeyFile),
- },
- shouldFail: true,
- expectedError: ":UNEXPECTED_MESSAGE:",
- expectedLocalError: "remote error: unexpected message",
- })
}
func addTLS13CipherPreferenceTests() {
diff --git a/src/ssl/test/test_config.cc b/src/ssl/test/test_config.cc
index 2d9f725..a5ce5a1 100644
--- a/src/ssl/test/test_config.cc
+++ b/src/ssl/test/test_config.cc
@@ -132,7 +132,6 @@
const Flag<std::string> kStringFlags[] = {
{ "-write-settings", &TestConfig::write_settings },
- { "-digest-prefs", &TestConfig::digest_prefs },
{ "-key-file", &TestConfig::key_file },
{ "-cert-file", &TestConfig::cert_file },
{ "-expect-server-name", &TestConfig::expected_server_name },
diff --git a/src/ssl/test/test_config.h b/src/ssl/test/test_config.h
index b742f94..ea12d34 100644
--- a/src/ssl/test/test_config.h
+++ b/src/ssl/test/test_config.h
@@ -26,7 +26,6 @@
int resume_count = 0;
std::string write_settings;
bool fallback_scsv = false;
- std::string digest_prefs;
std::vector<int> signing_prefs;
std::vector<int> verify_prefs;
std::string key_file;
diff --git a/src/ssl/tls13_client.cc b/src/ssl/tls13_client.cc
index 0013e61..688fa06 100644
--- a/src/ssl/tls13_client.cc
+++ b/src/ssl/tls13_client.cc
@@ -52,6 +52,7 @@
static enum ssl_hs_wait_t do_read_hello_retry_request(SSL_HANDSHAKE *hs) {
SSL *const ssl = hs->ssl;
+ assert(ssl->s3->have_version);
SSLMessage msg;
if (!ssl->method->get_message(ssl, &msg)) {
return ssl_hs_read_message;
@@ -60,6 +61,14 @@
CBS extensions;
uint16_t cipher_suite = 0;
if (ssl_is_draft22(ssl->version)) {
+ // Queue up a ChangeCipherSpec for whenever we next send something. This
+ // will be before the second ClientHello. If we offered early data, this was
+ // already done.
+ if (!hs->early_data_offered &&
+ !ssl->method->add_change_cipher_spec(ssl)) {
+ return ssl_hs_error;
+ }
+
if (!ssl_check_message_type(ssl, msg, SSL3_MT_SERVER_HELLO)) {
return ssl_hs_error;
}
@@ -95,8 +104,6 @@
(ssl_is_draft21(ssl->version) &&
!CBS_get_u16(&body, &cipher_suite)) ||
!CBS_get_u16_length_prefixed(&body, &extensions) ||
- // HelloRetryRequest may not be empty.
- CBS_len(&extensions) == 0 ||
CBS_len(&body) != 0) {
OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
@@ -146,6 +153,11 @@
ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNSUPPORTED_EXTENSION);
return ssl_hs_error;
}
+ if (!have_cookie && !have_key_share) {
+ OPENSSL_PUT_ERROR(SSL, SSL_R_EMPTY_HELLO_RETRY_REQUEST);
+ ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
+ return ssl_hs_error;
+ }
if (have_cookie) {
CBS cookie_value;
if (!CBS_get_u16_length_prefixed(&cookie, &cookie_value) ||
@@ -259,7 +271,14 @@
return ssl_hs_error;
}
- assert(ssl->s3->have_version);
+ // Forbid a second HelloRetryRequest.
+ if (ssl_is_draft22(ssl->version) &&
+ CBS_mem_equal(&server_random, kHelloRetryRequest, SSL3_RANDOM_SIZE)) {
+ ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE);
+ OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_MESSAGE);
+ return ssl_hs_error;
+ }
+
OPENSSL_memcpy(ssl->s3->server_random, CBS_data(&server_random),
SSL3_RANDOM_SIZE);
@@ -401,11 +420,17 @@
}
if (!hs->early_data_offered) {
+ // Earlier versions of the resumption experiment added ChangeCipherSpec just
+ // before the Finished flight.
+ if (ssl_is_resumption_client_ccs_experiment(ssl->version) &&
+ !ssl_is_draft22(ssl->version) &&
+ !ssl->method->add_change_cipher_spec(ssl)) {
+ return ssl_hs_error;
+ }
+
// If not sending early data, set client traffic keys now so that alerts are
// encrypted.
- if ((ssl_is_resumption_client_ccs_experiment(ssl->version) &&
- !ssl->method->add_change_cipher_spec(ssl)) ||
- !tls13_set_traffic_key(ssl, evp_aead_seal, hs->client_handshake_secret,
+ if (!tls13_set_traffic_key(ssl, evp_aead_seal, hs->client_handshake_secret,
hs->hash_len)) {
return ssl_hs_error;
}
diff --git a/src/ssl/tls_record.cc b/src/ssl/tls_record.cc
index 7e8e968..a1363fa 100644
--- a/src/ssl/tls_record.cc
+++ b/src/ssl/tls_record.cc
@@ -187,9 +187,25 @@
return ret;
}
-enum ssl_open_record_t tls_open_record(SSL *ssl, uint8_t *out_type,
- Span<uint8_t> *out, size_t *out_consumed,
- uint8_t *out_alert, Span<uint8_t> in) {
+static ssl_open_record_t skip_early_data(SSL *ssl, uint8_t *out_alert,
+ size_t consumed) {
+ ssl->s3->early_data_skipped += consumed;
+ if (ssl->s3->early_data_skipped < consumed) {
+ ssl->s3->early_data_skipped = kMaxEarlyDataSkipped + 1;
+ }
+
+ if (ssl->s3->early_data_skipped > kMaxEarlyDataSkipped) {
+ OPENSSL_PUT_ERROR(SSL, SSL_R_TOO_MUCH_SKIPPED_EARLY_DATA);
+ *out_alert = SSL_AD_UNEXPECTED_MESSAGE;
+ return ssl_open_record_error;
+ }
+
+ return ssl_open_record_discard;
+}
+
+ssl_open_record_t tls_open_record(SSL *ssl, uint8_t *out_type,
+ Span<uint8_t> *out, size_t *out_consumed,
+ uint8_t *out_alert, Span<uint8_t> in) {
*out_consumed = 0;
if (ssl->s3->read_shutdown == ssl_shutdown_close_notify) {
return ssl_open_record_close_notify;
@@ -267,7 +283,7 @@
if (ssl->s3->skip_early_data &&
ssl->s3->aead_read_ctx->is_null_cipher() &&
type == SSL3_RT_APPLICATION_DATA) {
- goto skipped_data;
+ return skip_early_data(ssl, out_alert, *out_consumed);
}
// Decrypt the body in-place.
@@ -276,7 +292,7 @@
MakeSpan(const_cast<uint8_t *>(CBS_data(&body)), CBS_len(&body)))) {
if (ssl->s3->skip_early_data && !ssl->s3->aead_read_ctx->is_null_cipher()) {
ERR_clear_error();
- goto skipped_data;
+ return skip_early_data(ssl, out_alert, *out_consumed);
}
OPENSSL_PUT_ERROR(SSL, SSL_R_DECRYPTION_FAILED_OR_BAD_RECORD_MAC);
@@ -292,8 +308,21 @@
}
// TLS 1.3 hides the record type inside the encrypted data.
- if (!ssl->s3->aead_read_ctx->is_null_cipher() &&
- ssl->s3->aead_read_ctx->ProtocolVersion() >= TLS1_3_VERSION) {
+ bool has_padding =
+ !ssl->s3->aead_read_ctx->is_null_cipher() &&
+ ssl->s3->aead_read_ctx->ProtocolVersion() >= TLS1_3_VERSION;
+
+ // If there is padding, the plaintext limit includes the padding, but includes
+ // extra room for the inner content type.
+ size_t plaintext_limit =
+ has_padding ? SSL3_RT_MAX_PLAIN_LENGTH + 1 : SSL3_RT_MAX_PLAIN_LENGTH;
+ if (out->size() > plaintext_limit) {
+ OPENSSL_PUT_ERROR(SSL, SSL_R_DATA_LENGTH_TOO_LONG);
+ *out_alert = SSL_AD_RECORD_OVERFLOW;
+ return ssl_open_record_error;
+ }
+
+ if (has_padding) {
// The outer record type is always application_data.
if (type != SSL3_RT_APPLICATION_DATA) {
OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_OUTER_RECORD_TYPE);
@@ -312,13 +341,6 @@
} while (type == 0);
}
- // Check the plaintext length.
- if (out->size() > SSL3_RT_MAX_PLAIN_LENGTH) {
- OPENSSL_PUT_ERROR(SSL, SSL_R_DATA_LENGTH_TOO_LONG);
- *out_alert = SSL_AD_RECORD_OVERFLOW;
- return ssl_open_record_error;
- }
-
// Limit the number of consecutive empty records.
if (out->empty()) {
ssl->s3->empty_record_count++;
@@ -358,20 +380,6 @@
*out_type = type;
return ssl_open_record_success;
-
-skipped_data:
- ssl->s3->early_data_skipped += *out_consumed;
- if (ssl->s3->early_data_skipped < *out_consumed) {
- ssl->s3->early_data_skipped = kMaxEarlyDataSkipped + 1;
- }
-
- if (ssl->s3->early_data_skipped > kMaxEarlyDataSkipped) {
- OPENSSL_PUT_ERROR(SSL, SSL_R_TOO_MUCH_SKIPPED_EARLY_DATA);
- *out_alert = SSL_AD_UNEXPECTED_MESSAGE;
- return ssl_open_record_error;
- }
-
- return ssl_open_record_discard;
}
static int do_seal_record(SSL *ssl, uint8_t *out_prefix, uint8_t *out,