blob: d1f1afdd14a5ad41f86e63bf684f524483629479 [file]
// Copyright 2026 Google LLC
//
// 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 "crypto/default/cms/ber_parser.h"
#include <cstring>
#include <limits>
#include <string>
#include "crypto/default/cms/cms_error_code.h"
#include "gtest/gtest.h"
#include "openssl/base.h"
#include "openssl/bytestring.h"
namespace credentio_cms {
namespace {
TEST(BerParser, NoData) {
BerParser parser(nullptr, 0);
EXPECT_TRUE(parser.ok());
EXPECT_TRUE(parser.IsNullOrEmpty());
EXPECT_EQ(ErrorCode::OK, parser.status());
EXPECT_EQ("", parser.error_message());
EXPECT_FALSE(parser.GetOptionalTag(0));
EXPECT_TRUE(parser.ok());
// Trying to read a tag should set an error.
parser.GetTag(CBS_ASN1_BOOLEAN);
EXPECT_FALSE(parser.ok());
EXPECT_EQ(ErrorCode::ASN1_PARSING_ERROR, parser.status());
}
TEST(BerParser, GetTagNonConstructedOk) {
uint8_t data[] = {// BOOLEAN { TRUE }
0x1, 1, 0xFF,
// OCTET STRING { "\x11\x22\x33" }
0x4, 3, 0x11, 0x22, 0x33};
BerParser parser(data, sizeof(data));
parser.GetTag(CBS_ASN1_BOOLEAN);
EXPECT_TRUE(parser.ok());
EXPECT_EQ(ErrorCode::OK, parser.status());
parser.GetTag(CBS_ASN1_OCTETSTRING);
EXPECT_TRUE(parser.ok());
EXPECT_EQ(ErrorCode::OK, parser.status());
// Reading past the end
parser.GetTag(CBS_ASN1_BOOLEAN);
EXPECT_FALSE(parser.ok());
EXPECT_EQ(ErrorCode::ASN1_PARSING_ERROR, parser.status());
// Reading the wrong tag.
BerParser parser2(data, sizeof(data));
parser2.GetTag(CBS_ASN1_OCTETSTRING);
EXPECT_FALSE(parser.ok());
EXPECT_EQ(ErrorCode::ASN1_PARSING_ERROR, parser.status());
}
TEST(BerParser, GetTagDefiniteLengthConstructedOk) {
uint8_t data[] = {// SEQUENCE {
0x30, 8,
// BOOLEAN { TRUE }
0x1, 1, 0xFF,
// OCTET STRING { "\x11\x22\x33" }
0x4, 3, 0x11, 0x22, 0x33,
// }
// INTEGER { 0x55 }
0x2, 1, 0x55};
BerParser parser(data, sizeof(data));
parser.GetTag(CBS_ASN1_SEQUENCE);
EXPECT_TRUE(parser.ok());
// We should now read inside the sequence.
parser.GetTag(CBS_ASN1_BOOLEAN);
EXPECT_TRUE(parser.ok());
parser.GetTag(CBS_ASN1_OCTETSTRING);
EXPECT_TRUE(parser.ok());
EXPECT_TRUE(parser.IsNullOrEmpty());
// Trying to read past the end of the sequence without calling CloseTag should
// fail.
parser.GetTag(CBS_ASN1_INTEGER);
EXPECT_FALSE(parser.ok());
EXPECT_EQ(ErrorCode::ASN1_PARSING_ERROR, parser.status());
}
TEST(BerParser, GetTagIndefiniteLengthConstructedOk) {
uint8_t data[] = {// SEQUENCE (indefinite) {
0x30, 0x80,
// BOOLEAN { TRUE }
0x1, 1, 0xFF,
// OCTET STRING { "\x11\x22\x33" }
0x4, 3, 0x11, 0x22, 0x33,
// }
0, 0,
// INTEGER { 0x55 }
0x2, 1, 0x55};
BerParser parser(data, sizeof(data));
parser.GetTag(CBS_ASN1_SEQUENCE);
EXPECT_TRUE(parser.ok());
// We should now read inside the sequence.
parser.GetTag(CBS_ASN1_BOOLEAN);
EXPECT_TRUE(parser.ok());
parser.GetTag(CBS_ASN1_OCTETSTRING);
EXPECT_TRUE(parser.ok());
EXPECT_TRUE(parser.IsNullOrEmpty());
// Trying to read past the end of the sequence without calling CloseTag should
// fail.
parser.GetTag(CBS_ASN1_INTEGER);
EXPECT_FALSE(parser.ok());
EXPECT_EQ(ErrorCode::ASN1_PARSING_ERROR, parser.status());
}
TEST(BerParser, GetTagNoDataInElement) {
// Making sure that an element with with no data is not seen as having
// indefinite length.
uint8_t data[] = {// NULL {}
5, 0,
// INTEGER { 0x55 }
2, 1, 0x55};
BerParser parser(data, sizeof(data));
parser.GetTag(5);
EXPECT_TRUE(parser.ok());
parser.GetTag(CBS_ASN1_INTEGER);
EXPECT_TRUE(parser.ok());
}
TEST(BerParser, GetTagBrokenEncoding) {
uint8_t data[] = {1, 2, 0xFF}; // BOOLEAN { TRUE }
BerParser parser(data, sizeof(data));
parser.GetTag(CBS_ASN1_SEQUENCE);
EXPECT_FALSE(parser.ok());
EXPECT_EQ(ErrorCode::ASN1_PARSING_ERROR, parser.status());
}
TEST(BerParser, GetTagMalformedData) {
uint8_t data[] = {1}; // BOOLEAN { missing value / error }
BerParser parser(data, sizeof(data));
parser.GetTag(CBS_ASN1_BOOLEAN);
EXPECT_FALSE(parser.ok());
EXPECT_EQ(ErrorCode::ASN1_PARSING_ERROR, parser.status());
}
TEST(BerParser, GetTagReadAfterError) {
uint8_t data[] = {// BOOLEAN { TRUE }
1, 1, 0xFF,
// OCTET STRING { "\x11\x22\x33" }
4, 3, 0x11, 0x22, 0x33};
BerParser parser(data, sizeof(data));
// Expecting the wrong tag.
parser.GetTag(CBS_ASN1_OCTETSTRING);
EXPECT_FALSE(parser.ok());
EXPECT_EQ(ErrorCode::ASN1_PARSING_ERROR, parser.status());
// Now that the parser is in an error state reading the correct tag
// should leave it in the error state.
parser.GetTag(CBS_ASN1_BOOLEAN);
EXPECT_FALSE(parser.ok());
EXPECT_EQ(ErrorCode::ASN1_PARSING_ERROR, parser.status());
}
TEST(BerParser, GetOptionalTag) {
uint8_t data[] = {// BOOLEAN { TRUE }
1, 1, 0xFF,
// OCTET STRING { "\x11\x22\x33" }
4, 3, 0x11, 0x22, 0x33};
BerParser parser(data, sizeof(data));
EXPECT_FALSE(parser.GetOptionalTag(CBS_ASN1_OCTETSTRING));
EXPECT_TRUE(parser.ok());
EXPECT_TRUE(parser.GetOptionalTag(CBS_ASN1_BOOLEAN));
EXPECT_TRUE(parser.ok());
EXPECT_FALSE(parser.GetOptionalTag(CBS_ASN1_BOOLEAN));
EXPECT_TRUE(parser.GetOptionalTag(CBS_ASN1_OCTETSTRING));
// Reading and optional tag at the end doesn't set an error.
EXPECT_FALSE(parser.GetOptionalTag(CBS_ASN1_OCTETSTRING));
EXPECT_TRUE(parser.ok());
}
TEST(BerParser, GetElementDataOk) {
uint8_t data[] = {// BOOLEAN { TRUE }
1, 1, 0xFF,
// OCTET STRING { "\x11\x22\x33" }
4, 3, 0x11, 0x22, 0x33};
BerParser parser(data, sizeof(data));
CBS out;
CBS_init(&out, data, 5); // harmless dummy values.
parser.GetElementData(CBS_ASN1_BOOLEAN, &out);
EXPECT_TRUE(parser.ok());
EXPECT_EQ(1, CBS_len(&out));
EXPECT_EQ(data + 2, CBS_data(&out));
parser.GetElementData(CBS_ASN1_OCTETSTRING, &out);
EXPECT_TRUE(parser.ok());
EXPECT_EQ(3, CBS_len(&out));
EXPECT_EQ(data + 5, CBS_data(&out));
// Read past the end, it should set an error and clear 'out'.
parser.GetElementData(CBS_ASN1_OCTETSTRING, &out);
EXPECT_FALSE(parser.ok());
EXPECT_EQ(0, CBS_len(&out));
EXPECT_EQ(nullptr, CBS_data(&out));
}
TEST(BerParser, GetElementDataReadAfterError) {
uint8_t data[] = {// BOOLEAN { TRUE }
1, 1, 0xFF,
// OCTET STRING { "\x11\x22\x33" }
4, 3, 0x11, 0x22, 0x33};
BerParser parser(data, sizeof(data));
CBS out;
CBS_init(&out, data, 5); // harmless dummy values.
// Expect the wrong tag.
parser.GetElementData(CBS_ASN1_OCTETSTRING, &out);
EXPECT_FALSE(parser.ok());
EXPECT_EQ(0, CBS_len(&out));
EXPECT_EQ(nullptr, CBS_data(&out));
// Now all reads will fail and 'out' will be empty.
parser.GetElementData(CBS_ASN1_BOOLEAN, &out);
EXPECT_FALSE(parser.ok());
EXPECT_EQ(0, CBS_len(&out));
EXPECT_EQ(nullptr, CBS_data(&out));
}
TEST(BerParser, GetElementDataConstructedDefiniteTag) {
uint8_t data[] = {// SEQUENCE {
0x30, 8,
// BOOLEAN { TRUE }
0x1, 1, 0xFF,
// OCTET STRING { "\x11\x22\x33" }
0x4, 3, 0x11, 0x22, 0x33,
// }
// INTEGER { 0x55 }
0x2, 1, 0x55};
BerParser parser(data, sizeof(data));
CBS out;
CBS_init(&out, data, 5);
parser.GetElementData(CBS_ASN1_SEQUENCE, &out);
EXPECT_TRUE(parser.ok());
EXPECT_EQ(8, CBS_len(&out));
EXPECT_EQ(data + 2, CBS_data(&out));
parser.GetElementData(CBS_ASN1_BOOLEAN, &out);
EXPECT_TRUE(parser.ok());
EXPECT_EQ(1, CBS_len(&out));
EXPECT_EQ(data + 4, CBS_data(&out));
EXPECT_EQ(0, memcmp(CBS_data(&out), "\xFF", 1));
parser.GetElementData(CBS_ASN1_OCTETSTRING, &out);
EXPECT_TRUE(parser.ok());
EXPECT_EQ(3, CBS_len(&out));
EXPECT_EQ(data + 7, CBS_data(&out));
EXPECT_EQ(0, memcmp(CBS_data(&out), "\x11\x22\x33", 3));
// Reading past the end of the sequence.
EXPECT_TRUE(parser.IsNullOrEmpty());
parser.GetElementData(CBS_ASN1_INTEGER, &out);
EXPECT_FALSE(parser.ok());
EXPECT_EQ(0, CBS_len(&out));
EXPECT_EQ(nullptr, CBS_data(&out));
}
TEST(BerParser, GetElementDataConstructedIndefiniteLengthTag) {
uint8_t data[] = {// SEQUENCE (indefinite) {
0x30, 0x80,
// BOOLEAN { TRUE }
0x1, 1, 0xFF,
// OCTET STRING { "\x11\x22\x33" }
0x4, 3, 0x11, 0x22, 0x33,
// }
0, 0,
// INTEGER { 0x55 }
0x2, 1, 0x55};
BerParser parser(data, sizeof(data));
CBS out;
CBS_init(&out, data, 5);
// We don't support reading element data for indefinite length elements.
parser.GetElementData(CBS_ASN1_SEQUENCE, &out);
EXPECT_FALSE(parser.ok());
EXPECT_EQ(0, CBS_len(&out));
EXPECT_EQ(nullptr, CBS_data(&out));
}
TEST(BerParse, GetElementDataNullPointer) {
uint8_t data[] = {1, 1, 0xFF}; // BOOLEAN { TRUE }
BerParser parser(data, sizeof(data));
parser.GetElementData(CBS_ASN1_BOOLEAN, nullptr);
EXPECT_FALSE(parser.ok());
EXPECT_EQ(ErrorCode::INVALID_PARAMETER, parser.status());
}
TEST(BerParser, GetElementDataMalformedData) {
uint8_t data[] = {1}; // BOOLEAN { missing value / error }
BerParser parser(data, sizeof(data));
parser.GetElementData(CBS_ASN1_BOOLEAN, nullptr);
EXPECT_FALSE(parser.ok());
EXPECT_EQ(ErrorCode::INVALID_PARAMETER, parser.status());
}
TEST(BerParser, GetOptionalElementData) {
uint8_t data[] = {1, 1, 0xFF}; // BOOLEAN { TRUE }
BerParser parser(data, sizeof(data));
CBS out;
CBS_init(&out, data, 5);
EXPECT_FALSE(parser.GetOptionalElementData(CBS_ASN1_OCTETSTRING, &out));
EXPECT_EQ(0, CBS_len(&out));
EXPECT_EQ(nullptr, CBS_data(&out));
// Not finding the optional tag should not set an error.
EXPECT_TRUE(parser.ok());
EXPECT_TRUE(parser.GetOptionalElementData(CBS_ASN1_BOOLEAN, &out));
EXPECT_EQ(1, CBS_len(&out));
EXPECT_EQ(0, memcmp(CBS_data(&out), "\xff", 1));
EXPECT_TRUE(parser.ok());
// Make sure that reading after an error clears 'out'
parser.GetTag(CBS_ASN1_OCTETSTRING);
EXPECT_FALSE(parser.ok());
CBS_init(&out, data, 5);
EXPECT_FALSE(parser.GetOptionalElementData(CBS_ASN1_BOOLEAN, &out));
EXPECT_EQ(0, CBS_len(&out));
EXPECT_EQ(nullptr, CBS_data(&out));
}
TEST(BerParse, GetOptionalElementDataNullPointer) {
uint8_t data[] = {1, 1, 0xFF}; // BOOLEAN { TRUE }
BerParser parser(data, sizeof(data));
parser.GetOptionalElementData(CBS_ASN1_BOOLEAN, nullptr);
EXPECT_FALSE(parser.ok());
EXPECT_EQ(ErrorCode::INVALID_PARAMETER, parser.status());
}
TEST(BerParser, GetAnyElementAndSkipChildrenConstructed) {
uint8_t data[] = {// SET {
0x31, 8,
// BOOLEAN { TRUE }
0x1, 1, 0xFF,
// OCTET STRING { "\x11\x22\x33" }
0x4, 3, 0x11, 0x22, 0x33,
// }
// INTEGER { 0x55 }
0x2, 1, 0x55};
BerParser parser(data, sizeof(data));
CBS out;
CBS_init(&out, data, 5);
parser.GetAnyElementAndSkipChildren(&out);
EXPECT_EQ(10, CBS_len(&out));
EXPECT_EQ(data, CBS_data(&out));
EXPECT_TRUE(parser.ok());
// The next read should be the INTEGER since the contents of the set are not
// parsed.
parser.GetAnyElementAndSkipChildren(&out);
EXPECT_EQ(3, CBS_len(&out));
EXPECT_EQ(0, memcmp(CBS_data(&out), "\x02\x01\x55", 1));
EXPECT_TRUE(parser.ok());
// And it should fail when reading past the end.
parser.GetAnyElementAndSkipChildren(&out);
EXPECT_FALSE(parser.ok());
EXPECT_EQ(0, CBS_len(&out));
EXPECT_EQ(nullptr, CBS_data(&out));
}
TEST(BerParser, GetAnyElementAndSkipChildrenConstructedIndefiniteLength) {
uint8_t data[] = {// SEQUENCE (indefinite) {
0x30, 0x80,
// BOOLEAN { TRUE }
0x1, 1, 0xFF,
// OCTET STRING { "\x11\x22\x33" }
0x4, 3, 0x11, 0x22, 0x33,
// }
0, 0,
// INTEGER { 0x55 }
0x2, 1, 0x55};
BerParser parser(data, sizeof(data));
CBS out;
CBS_init(&out, data, 5);
parser.GetAnyElementAndSkipChildren(&out);
EXPECT_FALSE(parser.ok());
EXPECT_EQ(0, CBS_len(&out));
EXPECT_EQ(nullptr, CBS_data(&out));
}
TEST(BerParser, GetAnyElementAndSkipChildrenReadAfterError) {
uint8_t data[] = {4, 3, 0x11, 0x22, 0x33}; // OCTET STRING { "\x11\x22\x33" }
BerParser parser(data, sizeof(data));
CBS out;
CBS_init(&out, data, 5);
parser.GetAnyElementAndSkipChildren(&out);
EXPECT_EQ(sizeof(data), CBS_len(&out));
EXPECT_EQ(data, CBS_data(&out));
EXPECT_TRUE(parser.ok());
BerParser parser2(data, sizeof(data));
parser2.GetTag(CBS_ASN1_SEQUENCE);
EXPECT_FALSE(parser2.ok());
// The read should fail and out be initialized.
parser2.GetAnyElementAndSkipChildren(&out);
EXPECT_FALSE(parser2.ok());
EXPECT_EQ(0, CBS_len(&out));
EXPECT_EQ(nullptr, CBS_data(&out));
}
TEST(BerParser, Peek) {
uint8_t data[] = {4, 3, 0x11, 0x22, 0x33}; // OCTET STRING { "\x11\x22\x33" }
BerParser parser(data, sizeof(data));
EXPECT_TRUE(parser.Peek(CBS_ASN1_OCTETSTRING));
// Peek doesn't read so it can be repeated.
EXPECT_TRUE(parser.Peek(CBS_ASN1_OCTETSTRING));
EXPECT_FALSE(parser.Peek(CBS_ASN1_INTEGER));
// Create an error condition.
parser.GetTag(CBS_ASN1_SEQUENCE);
EXPECT_FALSE(parser.ok());
EXPECT_FALSE(parser.Peek(CBS_ASN1_OCTETSTRING));
}
TEST(BerParser, IsNullOrEmpty) {
uint8_t data[] = {4, 3, 0x11, 0x22, 0x33}; // OCTET STRING { "\x11\x22\x33" }
BerParser parser(data, sizeof(data));
EXPECT_FALSE(parser.IsNullOrEmpty());
parser.GetTag(CBS_ASN1_OCTETSTRING);
EXPECT_TRUE(parser.IsNullOrEmpty());
BerParser parser2(nullptr, 0);
EXPECT_TRUE(parser2.IsNullOrEmpty());
}
TEST(BerParser, IsNullOrEmptyAfterError) {
uint8_t data[] = {4, 3, 0x11, 0x22, 0x33}; // OCTET STRING { "\x11\x22\x33" }
BerParser parser(data, sizeof(data));
EXPECT_FALSE(parser.IsNullOrEmpty());
parser.GetTag(CBS_ASN1_INTEGER);
EXPECT_FALSE(parser.ok());
EXPECT_TRUE(parser.IsNullOrEmpty());
}
TEST(BerParser, IsNullOrEmptyDefiniteLengthConstructed) {
uint8_t data[] = {// SEQUENCE {
0x30, 8,
// BOOLEAN { TRUE }
0x1, 1, 0xFF,
// OCTET STRING { "\x11\x22\x33" }
0x4, 3, 0x11, 0x22, 0x33,
// }
// INTEGER { 0x55 }
0x2, 1, 0x55};
BerParser parser(data, sizeof(data));
CBS out;
CBS_init(&out, data, 5);
parser.GetTag(CBS_ASN1_SEQUENCE);
EXPECT_FALSE(parser.IsNullOrEmpty());
parser.GetTag(CBS_ASN1_BOOLEAN);
EXPECT_FALSE(parser.IsNullOrEmpty());
parser.GetTag(CBS_ASN1_OCTETSTRING);
EXPECT_TRUE(parser.IsNullOrEmpty());
EXPECT_TRUE(parser.ok());
}
TEST(BerParser, IsNullOrEmptyConstructedWithNull) {
uint8_t data[] = {// SET {
0x31, 5,
// BOOLEAN { TRUE }
1, 1, 0xFF,
// NULL {}
5, 0,
// }
// INTEGER { 0x55 }
2, 1, 0x55};
BerParser parser(data, sizeof(data));
CBS out;
CBS_init(&out, data, 5);
parser.GetTag(CBS_ASN1_SET);
EXPECT_FALSE(parser.IsNullOrEmpty());
parser.GetTag(CBS_ASN1_BOOLEAN);
EXPECT_TRUE(parser.Peek(5 /* NULL */));
EXPECT_TRUE(parser.IsNullOrEmpty());
// Make sure that IsNullOrEmpty consumed the NULL.
EXPECT_FALSE(parser.Peek(5 /* NULL */));
EXPECT_TRUE(parser.ok());
}
TEST(BerParser, IsNullOrEmptyConstructedWithBrokenNull) {
uint8_t data[] = {// SET {
0x31, 5,
// BOOLEAN { TRUE }
1, 1, 0xFF,
// NULL with invalid length
5, 1,
// }
// INTEGER { 0x55 }
2, 1, 0x55};
BerParser parser(data, sizeof(data));
CBS out;
CBS_init(&out, data, 5);
parser.GetTag(CBS_ASN1_SET);
EXPECT_FALSE(parser.IsNullOrEmpty());
parser.GetTag(CBS_ASN1_BOOLEAN);
EXPECT_TRUE(parser.Peek(5 /* NULL */));
EXPECT_TRUE(parser.IsNullOrEmpty());
EXPECT_FALSE(parser.ok());
}
TEST(BerParser, IsNullOrEmptyIndefiniteLengthConstructed) {
uint8_t data[] = {// SEQUENCE (indefinite) {
0x30, 0x80,
// BOOLEAN { TRUE }
0x1, 1, 0xFF,
// OCTET STRING { "\x11\x22\x33" }
0x4, 3, 0x11, 0x22, 0x33,
// }
0, 0,
// INTEGER { 0x55 }
0x2, 1, 0x55};
BerParser parser(data, sizeof(data));
CBS out;
CBS_init(&out, data, 5);
parser.GetTag(CBS_ASN1_SEQUENCE);
EXPECT_FALSE(parser.IsNullOrEmpty());
parser.GetTag(CBS_ASN1_BOOLEAN);
EXPECT_FALSE(parser.IsNullOrEmpty());
parser.GetTag(CBS_ASN1_OCTETSTRING);
EXPECT_TRUE(parser.IsNullOrEmpty());
EXPECT_TRUE(parser.ok());
}
TEST(BerParser, GetInt) {
uint8_t data[] = {2, 1, 0x55}; // INTEGER { 0x55 }
BerParser parser(data, sizeof(data));
EXPECT_EQ(0x55, parser.GetInt());
EXPECT_TRUE(parser.ok());
// Reading after an error should just clear the value passed in.
EXPECT_EQ(0, parser.GetInt());
EXPECT_FALSE(parser.ok());
EXPECT_EQ(0, parser.GetInt());
EXPECT_FALSE(parser.ok());
}
TEST(BerParser, GetIntWrongTag) {
uint8_t data[] = {1, 1, 0x55}; // BOOLEAN { TRUE }
BerParser parser(data, sizeof(data));
EXPECT_EQ(0, parser.GetInt());
EXPECT_FALSE(parser.ok());
}
TEST(BerParser, GetIntNegative) {
uint8_t data[100] = {2 /* INTEGER */};
static_assert(sizeof(int) + 2 < sizeof(data), "The int size is too large");
data[1] = sizeof(int);
for (int i = 0; i < sizeof(int); ++i) {
data[i + 2] = 0xFF;
}
BerParser parser(data, sizeof(data));
EXPECT_EQ(0, parser.GetInt());
EXPECT_FALSE(parser.ok());
}
TEST(BerParser, GetIntMaxValue) {
uint8_t data[100] = {2 /* INTEGER */};
static_assert(sizeof(int) + 2 < sizeof(data), "The int size is too large");
data[1] = sizeof(int);
for (int i = 0; i < sizeof(int); ++i) {
data[i + 2] = 0xFF;
}
data[2] = 0x7F;
BerParser parser(data, sizeof(data));
EXPECT_EQ(std::numeric_limits<int>::max(), parser.GetInt());
EXPECT_TRUE(parser.ok());
}
TEST(BerParser, GetIntValueTooLarge) {
// The value doesn't fit in an uint64_t.
uint8_t data[100] = {2 /* INTEGER */, 9, 1};
static_assert(sizeof(uint64_t) == 8, "Unexpected uint64_t size.");
BerParser parser(data, sizeof(data));
EXPECT_EQ(0, parser.GetInt());
EXPECT_FALSE(parser.ok());
}
TEST(BerParser, GetIntMaxValuePlusOne) {
uint8_t data[100] = {2 /* INTEGER */};
static_assert(sizeof(int) + 2 < sizeof(data), "The int size is too large");
data[1] = sizeof(int);
data[2] = 0x80;
BerParser parser(data, sizeof(data));
EXPECT_EQ(0, parser.GetInt());
EXPECT_FALSE(parser.ok());
}
TEST(BerParser, EndConstructed) {
uint8_t data[] = {
// SET {
0x31, 5,
// OCTET STRING { "\x11\x22\x33" }
4, 3, 0x11, 0x22, 0x33,
// INTEGER { 0x55 }
2, 1, 0x55
// }
};
BerParser parser(data, sizeof(data));
parser.GetTag(CBS_ASN1_SET);
parser.GetTag(CBS_ASN1_OCTETSTRING);
EXPECT_FALSE(parser.Peek(CBS_ASN1_INTEGER));
parser.EndConstructed();
EXPECT_TRUE(parser.ok());
EXPECT_TRUE(parser.Peek(CBS_ASN1_INTEGER));
}
TEST(BerParser, EndConstructedTwoLevels) {
uint8_t data[] = {// SEQUENCE {
0x30, 10,
// SET {
0x31, 5,
// OCTET STRING { "\x11\x22\x33" }
4, 3, 0x11, 0x22, 0x33,
// }
// BOOLEAN { TRUE }
1, 1, 0xFF,
// }
// INTEGER { 0x55 }
2, 1, 0x55};
BerParser parser(data, sizeof(data));
parser.GetTag(CBS_ASN1_SEQUENCE);
parser.GetTag(CBS_ASN1_SET);
parser.GetTag(CBS_ASN1_OCTETSTRING);
EXPECT_FALSE(parser.Peek(CBS_ASN1_BOOLEAN));
parser.EndConstructed();
parser.GetTag(CBS_ASN1_BOOLEAN);
EXPECT_FALSE(parser.Peek(CBS_ASN1_INTEGER));
parser.EndConstructed();
parser.GetTag(CBS_ASN1_INTEGER);
EXPECT_TRUE(parser.ok());
}
TEST(BerParser, EndConstructedUnexpectedData) {
uint8_t data[] = {// SET {
0x31, 5,
// OCTET STRING { "\x11\x22\x33" }
4, 3, 0x11, 0x22, 0x33,
// }
// INTEGER { 0x55 }
2, 1, 0x55};
BerParser parser(data, sizeof(data));
parser.GetTag(CBS_ASN1_SET);
parser.EndConstructed();
EXPECT_FALSE(parser.ok());
}
TEST(BerParser, EndConstructedIndefiniteLength) {
uint8_t data[] = {// SEQUENCE (indefinite) {
0x30, 0x80,
// SET (indefinite) {
0x31, 0x80,
// OCTET STRING { "\x11\x22\x33" }
4, 3, 0x11, 0x22, 0x33,
// }
0, 0,
// BOOLEAN { TRUE }
1, 1, 0xFF,
// }
0, 0,
// INTEGER { 0x55 }
2, 1, 0x55};
BerParser parser(data, sizeof(data));
parser.GetTag(CBS_ASN1_SEQUENCE);
parser.GetTag(CBS_ASN1_SET);
parser.GetTag(CBS_ASN1_OCTETSTRING);
EXPECT_FALSE(parser.Peek(CBS_ASN1_BOOLEAN));
parser.EndConstructed();
parser.GetTag(CBS_ASN1_BOOLEAN);
EXPECT_FALSE(parser.Peek(CBS_ASN1_INTEGER));
parser.EndConstructed();
parser.GetTag(CBS_ASN1_INTEGER);
EXPECT_TRUE(parser.ok());
}
TEST(BerParser, EndConstructedIndefiniteLengthUnexpectedData) {
uint8_t data[] = {
// SET (indefinite) {
0x31,
0x80,
// OCTET STRING { "\x11\x22\x33" }
4,
3,
0x11,
0x22,
0x33,
// INTEGER { 0x55 }
2,
1,
0x55,
// The SET is missing the EOC.
};
BerParser parser(data, sizeof(data));
parser.GetTag(CBS_ASN1_SET);
parser.EndConstructed();
EXPECT_FALSE(parser.ok());
}
TEST(BerParser, EndConstructedMissingEOC) {
uint8_t data[] = {
// SET (indefinite) {
0x31,
0x80,
// OCTET STRING { "\x11\x22\x33" }
4,
3,
0x11,
0x22,
0x33,
// The SET is missing the EOC.
};
BerParser parser(data, sizeof(data));
parser.GetTag(CBS_ASN1_SET);
parser.GetTag(CBS_ASN1_OCTETSTRING);
parser.EndConstructed();
EXPECT_FALSE(parser.ok());
}
TEST(BerParser, EndConstructedIncompleteEOC) {
uint8_t data[] = {// SET (indefinite) {
0x31, 0x80,
// OCTET STRING { "\x11\x22\x33" }
4, 3, 0x11, 0x22, 0x33,
// Incomplete EOC for the SET.
0};
BerParser parser(data, sizeof(data));
parser.GetTag(CBS_ASN1_SET);
parser.GetTag(CBS_ASN1_OCTETSTRING);
parser.EndConstructed();
EXPECT_FALSE(parser.ok());
}
TEST(BerParser, EndConstructedIndefiniteLengthEOCInNextTag) {
uint8_t data[] = {
// SET {
0x31, 7,
// SET (indefinite) {
0x31, 0x80,
// OCTET STRING { "\x11\x22\x33" }
4, 3, 0x11, 0x22, 0x33,
// This is the EOC for the inner set, but it is past the end of the outer
// one, which is invalid.
0, 0};
BerParser parser(data, sizeof(data));
parser.GetTag(CBS_ASN1_SET);
parser.GetTag(CBS_ASN1_SET);
parser.GetTag(CBS_ASN1_OCTETSTRING);
EXPECT_TRUE(parser.ok());
parser.EndConstructed();
EXPECT_FALSE(parser.ok());
}
TEST(BerParser, EndConstructedTooManyCalls) {
BerParser parser(nullptr, 0);
EXPECT_TRUE(parser.ok());
parser.EndConstructed();
EXPECT_FALSE(parser.ok());
// This is the following structure, missing the SET's EOC:
//
// SET (indefinite) { OCTET STRING { "\x11" }
uint8_t data[] = {0x31, 0x80, 4, 1, 0x11};
BerParser parser2(data, sizeof(data));
parser.GetTag(CBS_ASN1_SET);
parser.GetTag(CBS_ASN1_OCTETSTRING);
parser.EndConstructed();
EXPECT_FALSE(parser.ok());
parser.EndConstructed();
EXPECT_FALSE(parser.ok());
}
TEST(BerParser, SetError) {
BerParser parser(nullptr, 0);
EXPECT_TRUE(parser.ok());
parser.SetError(ErrorCode::UNSUPPORTED, "foo");
EXPECT_EQ(ErrorCode::UNSUPPORTED, parser.status());
EXPECT_FALSE(parser.ok());
EXPECT_GE(parser.error_message().find("foo"), 0);
// Make sure that the error can't be reset.
parser.SetError(ErrorCode::OK, "bar");
EXPECT_EQ(ErrorCode::UNSUPPORTED, parser.status());
EXPECT_FALSE(parser.ok());
EXPECT_GE(parser.error_message().find("foo"), 0);
}
TEST(BerParser, SetElementName) {
uint8_t data[] = {// SET (indefinite {
0x31, 0x80,
// OCTET STRING { \x11 }
4, 1, 0x11,
// }
0, 0};
BerParser parser(data, sizeof(data));
parser.SetElementName("@1@");
parser.GetInt();
EXPECT_FALSE(parser.ok());
EXPECT_GE(parser.error_message().find("@1@"), 0);
BerParser parser2(data, sizeof(data));
parser2.SetElementName("@1@");
parser2.GetTag(CBS_ASN1_SET);
parser2.SetElementName("@2@");
parser2.GetInt();
EXPECT_FALSE(parser2.ok());
EXPECT_GE(parser2.error_message().find("@1@"), 0);
EXPECT_GE(parser2.error_message().find("@2@"), 0);
// Check that the names are not in the error string once their context is
// poped.
BerParser parser3(data, sizeof(data));
parser3.SetElementName("@1@");
parser3.GetTag(CBS_ASN1_SET);
parser3.SetElementName("@2@");
parser3.GetTag(CBS_ASN1_OCTETSTRING);
parser3.EndConstructed();
EXPECT_TRUE(parser3.ok());
parser3.GetInt();
EXPECT_FALSE(parser2.ok());
EXPECT_GE(parser3.error_message().find("@1@"), 0);
EXPECT_EQ(std::string::npos, parser3.error_message().find("@2@"));
}
} // namespace
} // namespace credentio_cms