| // 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 |