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