We use the standard Apache 2.0 file header, described in "APPENDIX: How to apply the Apache License to your work." This was primarily automated by running: git ls-tree -r --name-only HEAD | xargs go run ./util/relicense.go See go/boringssl-relicensing-triage for the results of triaging the output of the tool. As part of this, switch from taking fiat-crypto under MIT license to Apache 2.0. (It is licensed under MIT OR Apache-2.0 OR BSD-1-Clause.) The copyright_summary tool can also be used to confirm we didn't accidentally drop any copyright lines: # Run before the CL git grep -l Copyright | xargs go run ./util/copyright_summary.go -out /tmp/old.json # Run after the CL git grep -l Copyright | xargs go run ./util/copyright_summary.go -compare /tmp/old.json Bug: 364634028 Change-Id: I17c50e761e9d077a1f92e25969e50ed35e320c59 Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/75852 Reviewed-by: Bob Beck <bbe@google.com> Commit-Queue: David Benjamin <davidben@google.com> Reviewed-by: Adam Langley <agl@google.com>
378 lines
12 KiB
C++
378 lines
12 KiB
C++
// Copyright 2015 The Chromium Authors
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// https://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
#include "parser.h"
|
|
|
|
#include <gtest/gtest.h>
|
|
#include "input.h"
|
|
#include "parse_values.h"
|
|
|
|
BSSL_NAMESPACE_BEGIN
|
|
namespace der::test {
|
|
|
|
TEST(ParserTest, ConsumesAllBytesOfTLV) {
|
|
const uint8_t der[] = {0x04 /* OCTET STRING */, 0x00};
|
|
Parser parser((Input(der)));
|
|
CBS_ASN1_TAG tag;
|
|
Input value;
|
|
ASSERT_TRUE(parser.ReadTagAndValue(&tag, &value));
|
|
ASSERT_EQ(CBS_ASN1_OCTETSTRING, tag);
|
|
ASSERT_FALSE(parser.HasMore());
|
|
}
|
|
|
|
TEST(ParserTest, CanReadRawTLV) {
|
|
const uint8_t der[] = {0x02, 0x01, 0x01};
|
|
Parser parser((Input(der)));
|
|
Input tlv;
|
|
ASSERT_TRUE(parser.ReadRawTLV(&tlv));
|
|
ByteReader tlv_reader(tlv);
|
|
size_t tlv_len = tlv_reader.BytesLeft();
|
|
ASSERT_EQ(3u, tlv_len);
|
|
Input tlv_data;
|
|
ASSERT_TRUE(tlv_reader.ReadBytes(tlv_len, &tlv_data));
|
|
ASSERT_FALSE(parser.HasMore());
|
|
}
|
|
|
|
TEST(ParserTest, IgnoresContentsOfInnerValues) {
|
|
// This is a SEQUENCE which has one member. The member is another SEQUENCE
|
|
// with an invalid encoding - its length is too long.
|
|
const uint8_t der[] = {0x30, 0x02, 0x30, 0x7e};
|
|
Parser parser((Input(der)));
|
|
CBS_ASN1_TAG tag;
|
|
Input value;
|
|
ASSERT_TRUE(parser.ReadTagAndValue(&tag, &value));
|
|
}
|
|
|
|
TEST(ParserTest, FailsIfLengthOverlapsAnotherTLV) {
|
|
// This DER encoding has 2 top-level TLV tuples. The first is a SEQUENCE;
|
|
// the second is an INTEGER. The SEQUENCE contains an INTEGER, but its length
|
|
// is longer than what it has contents for.
|
|
const uint8_t der[] = {0x30, 0x02, 0x02, 0x01, 0x02, 0x01, 0x01};
|
|
Parser parser((Input(der)));
|
|
|
|
Parser inner_sequence;
|
|
ASSERT_TRUE(parser.ReadSequence(&inner_sequence));
|
|
uint64_t int_value;
|
|
ASSERT_TRUE(parser.ReadUint64(&int_value));
|
|
ASSERT_EQ(1u, int_value);
|
|
ASSERT_FALSE(parser.HasMore());
|
|
|
|
// Try to read the INTEGER from the SEQUENCE, which should fail.
|
|
CBS_ASN1_TAG tag;
|
|
Input value;
|
|
ASSERT_FALSE(inner_sequence.ReadTagAndValue(&tag, &value));
|
|
}
|
|
|
|
TEST(ParserTest, ReadOptionalTagPresent) {
|
|
// DER encoding of 2 top-level TLV values:
|
|
// INTEGER { 1 }
|
|
// OCTET_STRING { `02` }
|
|
const uint8_t der[] = {0x02, 0x01, 0x01, 0x04, 0x01, 0x02};
|
|
Parser parser((Input(der)));
|
|
|
|
Input value;
|
|
bool present;
|
|
ASSERT_TRUE(parser.ReadOptionalTag(CBS_ASN1_INTEGER, &value, &present));
|
|
ASSERT_TRUE(present);
|
|
const uint8_t expected_int_value[] = {0x01};
|
|
ASSERT_EQ(Input(expected_int_value), value);
|
|
|
|
CBS_ASN1_TAG tag;
|
|
ASSERT_TRUE(parser.ReadTagAndValue(&tag, &value));
|
|
ASSERT_EQ(CBS_ASN1_OCTETSTRING, tag);
|
|
const uint8_t expected_octet_string_value[] = {0x02};
|
|
ASSERT_EQ(Input(expected_octet_string_value), value);
|
|
|
|
ASSERT_FALSE(parser.HasMore());
|
|
}
|
|
|
|
TEST(ParserTest, ReadOptionalTag2Present) {
|
|
// DER encoding of 2 top-level TLV values:
|
|
// INTEGER { 1 }
|
|
// OCTET_STRING { `02` }
|
|
const uint8_t der[] = {0x02, 0x01, 0x01, 0x04, 0x01, 0x02};
|
|
Parser parser((Input(der)));
|
|
|
|
std::optional<Input> optional_value;
|
|
ASSERT_TRUE(parser.ReadOptionalTag(CBS_ASN1_INTEGER, &optional_value));
|
|
ASSERT_TRUE(optional_value.has_value());
|
|
const uint8_t expected_int_value[] = {0x01};
|
|
ASSERT_EQ(Input(expected_int_value), *optional_value);
|
|
|
|
CBS_ASN1_TAG tag;
|
|
Input value;
|
|
ASSERT_TRUE(parser.ReadTagAndValue(&tag, &value));
|
|
ASSERT_EQ(CBS_ASN1_OCTETSTRING, tag);
|
|
const uint8_t expected_octet_string_value[] = {0x02};
|
|
ASSERT_EQ(Input(expected_octet_string_value), value);
|
|
|
|
ASSERT_FALSE(parser.HasMore());
|
|
}
|
|
|
|
TEST(ParserTest, ReadOptionalTagNotPresent) {
|
|
// DER encoding of 1 top-level TLV value:
|
|
// OCTET_STRING { `02` }
|
|
const uint8_t der[] = {0x04, 0x01, 0x02};
|
|
Parser parser((Input(der)));
|
|
|
|
Input value;
|
|
bool present;
|
|
ASSERT_TRUE(parser.ReadOptionalTag(CBS_ASN1_INTEGER, &value, &present));
|
|
ASSERT_FALSE(present);
|
|
|
|
CBS_ASN1_TAG tag;
|
|
ASSERT_TRUE(parser.ReadTagAndValue(&tag, &value));
|
|
ASSERT_EQ(CBS_ASN1_OCTETSTRING, tag);
|
|
const uint8_t expected_octet_string_value[] = {0x02};
|
|
ASSERT_EQ(Input(expected_octet_string_value), value);
|
|
|
|
ASSERT_FALSE(parser.HasMore());
|
|
}
|
|
|
|
TEST(ParserTest, ReadOptionalTag2NotPresent) {
|
|
// DER encoding of 1 top-level TLV value:
|
|
// OCTET_STRING { `02` }
|
|
const uint8_t der[] = {0x04, 0x01, 0x02};
|
|
Parser parser((Input(der)));
|
|
|
|
std::optional<Input> optional_value;
|
|
ASSERT_TRUE(parser.ReadOptionalTag(CBS_ASN1_INTEGER, &optional_value));
|
|
ASSERT_FALSE(optional_value.has_value());
|
|
|
|
CBS_ASN1_TAG tag;
|
|
Input value;
|
|
ASSERT_TRUE(parser.ReadTagAndValue(&tag, &value));
|
|
ASSERT_EQ(CBS_ASN1_OCTETSTRING, tag);
|
|
const uint8_t expected_octet_string_value[] = {0x02};
|
|
ASSERT_EQ(Input(expected_octet_string_value), value);
|
|
|
|
ASSERT_FALSE(parser.HasMore());
|
|
}
|
|
|
|
TEST(ParserTest, CanSkipOptionalTagAtEndOfInput) {
|
|
const uint8_t der[] = {0x02 /* INTEGER */, 0x01, 0x01};
|
|
Parser parser((Input(der)));
|
|
|
|
CBS_ASN1_TAG tag;
|
|
Input value;
|
|
ASSERT_TRUE(parser.ReadTagAndValue(&tag, &value));
|
|
bool present;
|
|
ASSERT_TRUE(parser.ReadOptionalTag(CBS_ASN1_INTEGER, &value, &present));
|
|
ASSERT_FALSE(present);
|
|
ASSERT_FALSE(parser.HasMore());
|
|
}
|
|
|
|
TEST(ParserTest, SkipOptionalTagDoesntConsumePresentNonMatchingTLVs) {
|
|
const uint8_t der[] = {0x02 /* INTEGER */, 0x01, 0x01};
|
|
Parser parser((Input(der)));
|
|
|
|
bool present;
|
|
ASSERT_TRUE(parser.SkipOptionalTag(CBS_ASN1_OCTETSTRING, &present));
|
|
ASSERT_FALSE(present);
|
|
ASSERT_TRUE(parser.SkipOptionalTag(CBS_ASN1_INTEGER, &present));
|
|
ASSERT_TRUE(present);
|
|
ASSERT_FALSE(parser.HasMore());
|
|
}
|
|
|
|
TEST(ParserTest, TagNumbersAboveThirtySupported) {
|
|
// Context-specific class, tag number 31, length 0.
|
|
const uint8_t der[] = {0x9f, 0x1f, 0x00};
|
|
Parser parser((Input(der)));
|
|
|
|
CBS_ASN1_TAG tag;
|
|
Input value;
|
|
ASSERT_TRUE(parser.ReadTagAndValue(&tag, &value));
|
|
EXPECT_EQ(CBS_ASN1_CONTEXT_SPECIFIC | 31u, tag);
|
|
ASSERT_FALSE(parser.HasMore());
|
|
}
|
|
|
|
TEST(ParserTest, ParseTags) {
|
|
{
|
|
// Universal primitive tag, tag number 4.
|
|
const uint8_t der[] = {0x04, 0x00};
|
|
Parser parser((Input(der)));
|
|
|
|
CBS_ASN1_TAG tag;
|
|
Input value;
|
|
ASSERT_TRUE(parser.ReadTagAndValue(&tag, &value));
|
|
EXPECT_EQ(CBS_ASN1_OCTETSTRING, tag);
|
|
}
|
|
|
|
{
|
|
// Universal constructed tag, tag number 16.
|
|
const uint8_t der[] = {0x30, 0x00};
|
|
Parser parser((Input(der)));
|
|
|
|
CBS_ASN1_TAG tag;
|
|
Input value;
|
|
ASSERT_TRUE(parser.ReadTagAndValue(&tag, &value));
|
|
EXPECT_EQ(CBS_ASN1_SEQUENCE, tag);
|
|
}
|
|
|
|
{
|
|
// Application primitive tag, tag number 1.
|
|
const uint8_t der[] = {0x41, 0x00};
|
|
Parser parser((Input(der)));
|
|
|
|
CBS_ASN1_TAG tag;
|
|
Input value;
|
|
ASSERT_TRUE(parser.ReadTagAndValue(&tag, &value));
|
|
EXPECT_EQ(CBS_ASN1_APPLICATION | 1, tag);
|
|
}
|
|
|
|
{
|
|
// Context-specific constructed tag, tag number 30.
|
|
const uint8_t der[] = {0xbe, 0x00};
|
|
Parser parser((Input(der)));
|
|
|
|
CBS_ASN1_TAG tag;
|
|
Input value;
|
|
ASSERT_TRUE(parser.ReadTagAndValue(&tag, &value));
|
|
EXPECT_EQ(CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 30, tag);
|
|
}
|
|
|
|
{
|
|
// Private primitive tag, tag number 15.
|
|
const uint8_t der[] = {0xcf, 0x00};
|
|
Parser parser((Input(der)));
|
|
|
|
CBS_ASN1_TAG tag;
|
|
Input value;
|
|
ASSERT_TRUE(parser.ReadTagAndValue(&tag, &value));
|
|
EXPECT_EQ(CBS_ASN1_PRIVATE | 15, tag);
|
|
}
|
|
}
|
|
|
|
TEST(ParserTest, IncompleteEncodingTagOnly) {
|
|
const uint8_t der[] = {0x01};
|
|
Parser parser((Input(der)));
|
|
|
|
CBS_ASN1_TAG tag;
|
|
Input value;
|
|
ASSERT_FALSE(parser.ReadTagAndValue(&tag, &value));
|
|
ASSERT_TRUE(parser.HasMore());
|
|
}
|
|
|
|
TEST(ParserTest, IncompleteEncodingLengthTruncated) {
|
|
// Tag: octet string; length: long form, should have 2 total octets, but
|
|
// the last one is missing. (There's also no value.)
|
|
const uint8_t der[] = {0x04, 0x81};
|
|
Parser parser((Input(der)));
|
|
|
|
CBS_ASN1_TAG tag;
|
|
Input value;
|
|
ASSERT_FALSE(parser.ReadTagAndValue(&tag, &value));
|
|
ASSERT_TRUE(parser.HasMore());
|
|
}
|
|
|
|
TEST(ParserTest, IncompleteEncodingValueShorterThanLength) {
|
|
// Tag: octet string; length: 2; value: first octet 'T', second octet missing.
|
|
const uint8_t der[] = {0x04, 0x02, 0x84};
|
|
Parser parser((Input(der)));
|
|
|
|
CBS_ASN1_TAG tag;
|
|
Input value;
|
|
ASSERT_FALSE(parser.ReadTagAndValue(&tag, &value));
|
|
ASSERT_TRUE(parser.HasMore());
|
|
}
|
|
|
|
TEST(ParserTest, LengthMustBeEncodedWithMinimumNumberOfOctets) {
|
|
const uint8_t der[] = {0x01, 0x81, 0x01, 0x00};
|
|
Parser parser((Input(der)));
|
|
|
|
CBS_ASN1_TAG tag;
|
|
Input value;
|
|
ASSERT_FALSE(parser.ReadTagAndValue(&tag, &value));
|
|
ASSERT_TRUE(parser.HasMore());
|
|
}
|
|
|
|
TEST(ParserTest, LengthMustNotHaveLeadingZeroes) {
|
|
// Tag: octet string; length: 3 bytes of length encoding a value of 128
|
|
// (it should be encoded in only 2 bytes). Value: 128 bytes of 0.
|
|
const uint8_t der[] = {
|
|
0x04, 0x83, 0x80, 0x81, 0x80, // group the 0s separately
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
|
|
Parser parser((Input(der)));
|
|
|
|
CBS_ASN1_TAG tag;
|
|
Input value;
|
|
ASSERT_FALSE(parser.ReadTagAndValue(&tag, &value));
|
|
ASSERT_TRUE(parser.HasMore());
|
|
}
|
|
|
|
TEST(ParserTest, ReadConstructedFailsForNonConstructedTags) {
|
|
// Tag number is for SEQUENCE, but the constructed bit isn't set.
|
|
const uint8_t der[] = {0x10, 0x00};
|
|
Parser parser((Input(der)));
|
|
|
|
CBS_ASN1_TAG expected_tag = 0x10;
|
|
Parser sequence_parser;
|
|
ASSERT_FALSE(parser.ReadConstructed(expected_tag, &sequence_parser));
|
|
|
|
// Check that we didn't fail above because of a tag mismatch or an improperly
|
|
// encoded TLV.
|
|
Input value;
|
|
ASSERT_TRUE(parser.ReadTag(expected_tag, &value));
|
|
ASSERT_FALSE(parser.HasMore());
|
|
}
|
|
|
|
TEST(ParserTest, CannotAdvanceAfterReadOptionalTag) {
|
|
const uint8_t der[] = {0x02, 0x01, 0x01};
|
|
Parser parser((Input(der)));
|
|
|
|
Input value;
|
|
bool present;
|
|
ASSERT_TRUE(parser.ReadOptionalTag(0x04, &value, &present));
|
|
ASSERT_FALSE(present);
|
|
ASSERT_FALSE(parser.Advance());
|
|
}
|
|
|
|
// Reads a valid BIT STRING with 1 unused bit.
|
|
TEST(ParserTest, ReadBitString) {
|
|
const uint8_t der[] = {0x03, 0x03, 0x01, 0xAA, 0xBE};
|
|
Parser parser((Input(der)));
|
|
|
|
std::optional<BitString> bit_string = parser.ReadBitString();
|
|
ASSERT_TRUE(bit_string.has_value());
|
|
EXPECT_FALSE(parser.HasMore());
|
|
|
|
EXPECT_EQ(1u, bit_string->unused_bits());
|
|
ASSERT_EQ(2u, bit_string->bytes().size());
|
|
EXPECT_EQ(0xAA, bit_string->bytes()[0]);
|
|
EXPECT_EQ(0xBE, bit_string->bytes()[1]);
|
|
}
|
|
|
|
// Tries reading a BIT STRING. This should fail because the tag is not for a
|
|
// BIT STRING.
|
|
TEST(ParserTest, ReadBitStringBadTag) {
|
|
const uint8_t der[] = {0x05, 0x03, 0x01, 0xAA, 0xBE};
|
|
Parser parser((Input(der)));
|
|
|
|
std::optional<BitString> bit_string = parser.ReadBitString();
|
|
EXPECT_FALSE(bit_string.has_value());
|
|
}
|
|
|
|
} // namespace der::test
|
|
BSSL_NAMESPACE_END
|