blob: c7b10e33bca0bba45aa80931668e5e3490067c8d [file] [log] [blame]
// Copyright 2017 The Chromium Authors. All rights reserved.
// Copyright (C) 2018 Apple Inc. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "config.h"
#if ENABLE(WEB_AUTHN)
#include <WebCore/CBORReader.h>
#include <limits>
#include <utility>
// Leveraging RFC 7049 examples from https://github.com/cbor/test-vectors/blob/master/appendix_a.json.
namespace TestWebKitAPI {
using namespace cbor;
TEST(CBORReaderTest, TestReadUint)
{
struct UintTestCase {
const int64_t value;
const Vector<uint8_t> cborData;
};
static const UintTestCase kUintTestCases[] = {
{ 0, { 0x00 } },
{ 1, { 0x01 } },
{ 23, { 0x17 } },
{ 24, { 0x18, 0x18 } },
{ std::numeric_limits<uint8_t>::max(), { 0x18, 0xff } },
{ 1LL << 8, { 0x19, 0x01, 0x00 } },
{ std::numeric_limits<uint16_t>::max(), { 0x19, 0xff, 0xff } },
{ 1LL << 16, { 0x1a, 0x00, 0x01, 0x00, 0x00 } },
{ std::numeric_limits<uint32_t>::max(), { 0x1a, 0xff, 0xff, 0xff, 0xff } },
{ 1LL << 32, { 0x1b, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00 } },
{ std::numeric_limits<int64_t>::max(),
{ 0x1b, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF } },
};
for (const UintTestCase& testCase : kUintTestCases) {
std::optional<CBORValue> cbor = CBORReader::read(testCase.cborData);
ASSERT_TRUE(cbor.has_value());
ASSERT_TRUE(cbor.value().type() == CBORValue::Type::Unsigned);
EXPECT_EQ(cbor.value().getInteger(), testCase.value);
}
}
TEST(CBORReaderTest, TestUintEncodedWithNonMinimumByteLength)
{
static const Vector<uint8_t> nonMinimalUintEncodings[] = {
// Uint 23 encoded with 1 byte.
{ 0x18, 0x17 },
// Uint 255 encoded with 2 bytes.
{ 0x19, 0x00, 0xff },
// Uint 65535 encoded with 4 byte.
{ 0x1a, 0x00, 0x00, 0xff, 0xff },
// Uint 4294967295 encoded with 8 byte.
{ 0x1b, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff },
// When decoding byte has more than one syntax error, the first syntax
// error encountered during deserialization is returned as the error code.
{
0xa2, // map with non-minimally encoded key
0x17, // key 24
0x61, 0x42, // value :"B"
0x18, 0x17, // key 23 encoded with extra byte
0x61, 0x45 // value "E"
},
{
0xa2, // map with out of order and non-minimally encoded key
0x18, 0x17, // key 23 encoded with extra byte
0x61, 0x45, // value "E"
0x17, // key 23
0x61, 0x42 // value :"B"
},
{
0xa2, // map with duplicate non-minimally encoded key
0x18, 0x17, // key 23 encoded with extra byte
0x61, 0x45, // value "E"
0x18, 0x17, // key 23 encoded with extra byte
0x61, 0x42 // value :"B"
},
};
CBORReader::DecoderError errorCode;
for (const auto& nonMinimalUint : nonMinimalUintEncodings) {
std::optional<CBORValue> cbor = CBORReader::read(nonMinimalUint, &errorCode);
EXPECT_FALSE(cbor.has_value());
EXPECT_TRUE(errorCode == CBORReader::DecoderError::NonMinimalCBOREncoding);
}
}
TEST(CBORReaderTest, TestReadNegativeInt)
{
struct NegativeIntTestCase {
const int64_t negativeInt;
const Vector<uint8_t> cborData;
};
static const NegativeIntTestCase kNegativeIntTestCases[] = {
{ -1LL, { 0x20 } },
{ -24LL, { 0x37 } },
{ -25LL, { 0x38, 0x18 } },
{ -256LL, { 0x38, 0xff } },
{ -1000LL, { 0x39, 0x03, 0xe7 } },
{ -1000000LL, { 0x3a, 0x00, 0x0f, 0x42, 0x3f } },
{ -4294967296LL, { 0x3a, 0xff, 0xff, 0xff, 0xff } },
{ std::numeric_limits<int64_t>::min(),
{ 0x3b, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } }
};
for (const NegativeIntTestCase& testCase : kNegativeIntTestCases) {
std::optional<CBORValue> cbor = CBORReader::read(testCase.cborData);
ASSERT_TRUE(cbor.has_value());
ASSERT_TRUE(cbor.value().type() == CBORValue::Type::Negative);
EXPECT_EQ(cbor.value().getInteger(), testCase.negativeInt);
}
}
TEST(CBORReaderTest, TestReadBytes)
{
struct ByteTestCase {
const Vector<uint8_t> value;
const Vector<uint8_t> cborData;
};
static const ByteTestCase kByteStringTestCases[] = {
{{ }, {0x40}},
{{0x01, 0x02, 0x03, 0x04}, {0x44, 0x01, 0x02, 0x03, 0x04}},
};
for (const ByteTestCase& testCase : kByteStringTestCases) {
std::optional<CBORValue> cbor = CBORReader::read(testCase.cborData);
ASSERT_TRUE(cbor.has_value());
ASSERT_TRUE(cbor.value().type() == CBORValue::Type::ByteString);
EXPECT_TRUE(cbor.value().getByteString() == testCase.value);
}
}
TEST(CBORReaderTest, TestReadString)
{
struct StringTestCase {
const String value;
const Vector<uint8_t> cborData;
};
static const StringTestCase kStringTestCases[] = {
{ emptyString(), { 0x60 } },
{ "a"_s, { 0x61, 0x61 } },
{ "IETF"_s, { 0x64, 0x49, 0x45, 0x54, 0x46 } },
{ "\"\\"_s, { 0x62, 0x22, 0x5c } },
{ String::fromUTF8("\xc3\xbc"), { 0x62, 0xc3, 0xbc } },
{ String::fromUTF8("\xe6\xb0\xb4"), { 0x63, 0xe6, 0xb0, 0xb4 } },
{ String::fromUTF8("\xf0\x90\x85\x91"), { 0x64, 0xf0, 0x90, 0x85, 0x91 } },
};
for (const StringTestCase& testCase : kStringTestCases) {
std::optional<CBORValue> cbor = CBORReader::read(testCase.cborData);
ASSERT_TRUE(cbor.has_value());
ASSERT_TRUE(cbor.value().type() == CBORValue::Type::String);
EXPECT_TRUE(cbor.value().getString() == testCase.value);
}
}
TEST(CBORReaderTest, TestReadStringWithNUL)
{
static const struct {
const String value;
const Vector<uint8_t> cborData;
} kStringTestCases[] = {
{ "string_without_nul"_str,
{ 0x72, 0x73, 0x74, 0x72, 0x69, 0x6E, 0x67, 0x5F, 0x77, 0x69, 0x74, 0x68,
0x6F, 0x75, 0x74, 0x5F, 0x6E, 0x75, 0x6C } },
{ String("nul_terminated_string\0", 22),
{ 0x76, 0x6E, 0x75, 0x6C, 0x5F, 0x74, 0x65, 0x72, 0x6D, 0x69, 0x6E, 0x61,
0x74, 0x65, 0x64, 0x5F, 0x73, 0x74, 0x72, 0x69, 0x6E, 0x67, 0x00 } },
{ String("embedded\0nul", 12),
{ 0x6C, 0x65, 0x6D, 0x62, 0x65, 0x64, 0x64, 0x65, 0x64, 0x00, 0x6E, 0x75,
0x6C } },
{ String("trailing_nuls\0\0", 15),
{ 0x6F, 0x74, 0x72, 0x61, 0x69, 0x6C, 0x69, 0x6E, 0x67, 0x5F, 0x6E, 0x75,
0x6C, 0x73, 0x00, 0x00 } },
};
for (const auto& testCase : kStringTestCases) {
std::optional<CBORValue> cbor = CBORReader::read(testCase.cborData);
ASSERT_TRUE(cbor.has_value());
ASSERT_TRUE(cbor.value().type() == CBORValue::Type::String);
EXPECT_TRUE(cbor.value().getString() == testCase.value);
}
}
TEST(CBORReaderTest, TestReadStringWithInvalidByteSequenceAfterNUL)
{
// UTF-8 validation should not stop at the first NUL character in the string.
// That is, a string with an invalid byte sequence should fail UTF-8
// validation even if the invalid character is located after one or more NUL
// characters. Here, 0xA6 is an unexpected continuation byte.
static const Vector<uint8_t> stringWithInvalidContinuationByte = {
0x63, 0x00, 0x00, 0xA6
};
CBORReader::DecoderError errorCode;
std::optional<CBORValue> cbor = CBORReader::read(stringWithInvalidContinuationByte, &errorCode);
EXPECT_FALSE(cbor.has_value());
EXPECT_TRUE(errorCode == CBORReader::DecoderError::InvalidUTF8);
}
TEST(CBORReaderTest, TestReadArray)
{
static const Vector<uint8_t> kArrayTestCaseCBOR = {
0x98, 0x19, // array of 25 elements
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c,
0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
0x18, 0x18, 0x19,
};
std::optional<CBORValue> cbor = CBORReader::read(kArrayTestCaseCBOR);
ASSERT_TRUE(cbor.has_value());
const CBORValue cborArray = WTFMove(cbor.value());
ASSERT_TRUE(cborArray.type() == CBORValue::Type::Array);
ASSERT_EQ(cborArray.getArray().size(), 25u);
Vector<CBORValue> array;
for (int i = 0; i < 25; i++) {
ASSERT_TRUE(cborArray.getArray()[i].type() == CBORValue::Type::Unsigned);
EXPECT_EQ(cborArray.getArray()[i].getInteger(), static_cast<int64_t>(i + 1));
}
}
TEST(CBORReaderTest, TestReadMapWithMapValue)
{
static const Vector<uint8_t> kMapTestCaseCBOR = {
0xa4, // map with 4 key value pairs:
0x18, 0x18, // 24
0x63, 0x61, 0x62, 0x63, // "abc"
0x60, // ""
0x61, 0x2e, // "."
0x61, 0x62, // "b"
0x61, 0x42, // "B"
0x62, 0x61, 0x61, // "aa"
0x62, 0x41, 0x41, // "AA"
};
std::optional<CBORValue> cbor = CBORReader::read(kMapTestCaseCBOR);
ASSERT_TRUE(cbor.has_value());
const CBORValue cborVal = WTFMove(cbor.value());
ASSERT_TRUE(cborVal.type() == CBORValue::Type::Map);
ASSERT_EQ(cborVal.getMap().size(), 4u);
const CBORValue keyUint(24);
ASSERT_EQ(cborVal.getMap().count(keyUint), 1u);
ASSERT_TRUE(cborVal.getMap().find(keyUint)->second.type() == CBORValue::Type::String);
EXPECT_TRUE(cborVal.getMap().find(keyUint)->second.getString() == "abc"_s);
const CBORValue keyEmptyString("");
ASSERT_EQ(cborVal.getMap().count(keyEmptyString), 1u);
ASSERT_TRUE(cborVal.getMap().find(keyEmptyString)->second.type() == CBORValue::Type::String);
EXPECT_TRUE(cborVal.getMap().find(keyEmptyString)->second.getString() == "."_s);
const CBORValue keyB("b");
ASSERT_EQ(cborVal.getMap().count(keyB), 1u);
ASSERT_TRUE(cborVal.getMap().find(keyB)->second.type() == CBORValue::Type::String);
EXPECT_TRUE(cborVal.getMap().find(keyB)->second.getString() == "B"_s);
const CBORValue keyAa("aa");
ASSERT_EQ(cborVal.getMap().count(keyAa), 1u);
ASSERT_TRUE(cborVal.getMap().find(keyAa)->second.type() == CBORValue::Type::String);
EXPECT_TRUE(cborVal.getMap().find(keyAa)->second.getString() == "AA"_s);
}
TEST(CBORReaderTest, TestReadMapWithIntegerKeys)
{
static const Vector<uint8_t> kMapWithIntegerKeyCBOR = {
0xA4, // map with 4 key value pairs
0x01, // key : 1
0x61, 0x61, // value : "a"
0x09, // key : 9
0x61, 0x62, // value : "b"
0x19, 0x03, 0xE7, // key : 999
0x61, 0x63, // value "c"
0x19, 0x04, 0x57, // key : 1111
0x61, 0x64, // value : "d"
};
std::optional<CBORValue> cbor = CBORReader::read(kMapWithIntegerKeyCBOR);
ASSERT_TRUE(cbor.has_value());
const CBORValue cborVal = WTFMove(cbor.value());
ASSERT_TRUE(cborVal.type() == CBORValue::Type::Map);
ASSERT_EQ(cborVal.getMap().size(), 4u);
const CBORValue key1(1);
ASSERT_EQ(cborVal.getMap().count(key1), 1u);
ASSERT_TRUE(cborVal.getMap().find(key1)->second.type() == CBORValue::Type::String);
ASSERT_TRUE(cborVal.getMap().find(key1)->second.getString() == "a"_s);
const CBORValue key9(9);
ASSERT_EQ(cborVal.getMap().count(key9), 1u);
ASSERT_TRUE(cborVal.getMap().find(key9)->second.type() == CBORValue::Type::String);
ASSERT_TRUE(cborVal.getMap().find(key9)->second.getString() == "b"_s);
const CBORValue key999(999);
ASSERT_EQ(cborVal.getMap().count(key999), 1u);
ASSERT_TRUE(cborVal.getMap().find(key999)->second.type() == CBORValue::Type::String);
ASSERT_TRUE(cborVal.getMap().find(key999)->second.getString() == "c"_s);
const CBORValue key1111(1111);
ASSERT_EQ(cborVal.getMap().count(key1111), 1u);
ASSERT_TRUE(cborVal.getMap().find(key1111)->second.type() == CBORValue::Type::String);
ASSERT_TRUE(cborVal.getMap().find(key1111)->second.getString() == "d"_s);
}
TEST(CBORReaderTest, TestReadMapWithArray)
{
static const Vector<uint8_t> kMapArrayTestCaseCBOR = {
0xa2, // map of 2 pairs
0x61, 0x61, // "a"
0x01,
0x61, 0x62, // "b"
0x82, // array with 2 elements
0x02,
0x03,
};
std::optional<CBORValue> cbor = CBORReader::read(kMapArrayTestCaseCBOR);
ASSERT_TRUE(cbor.has_value());
const CBORValue cborVal = WTFMove(cbor.value());
ASSERT_TRUE(cborVal.type() == CBORValue::Type::Map);
ASSERT_EQ(cborVal.getMap().size(), 2u);
const CBORValue keyA("a");
ASSERT_EQ(cborVal.getMap().count(keyA), 1u);
ASSERT_TRUE(cborVal.getMap().find(keyA)->second.type() == CBORValue::Type::Unsigned);
EXPECT_EQ(cborVal.getMap().find(keyA)->second.getInteger(), 1u);
const CBORValue keyB("b");
ASSERT_EQ(cborVal.getMap().count(keyB), 1u);
ASSERT_TRUE(cborVal.getMap().find(keyB)->second.type() == CBORValue::Type::Array);
const CBORValue nestedArray = cborVal.getMap().find(keyB)->second.clone();
ASSERT_EQ(nestedArray.getArray().size(), 2u);
for (int i = 0; i < 2; i++) {
ASSERT_TRUE(nestedArray.getArray()[i].type() == CBORValue::Type::Unsigned);
EXPECT_EQ(nestedArray.getArray()[i].getInteger(), static_cast<int64_t>(i + 2));
}
}
TEST(CBORReaderTest, TestReadNestedMap)
{
static const Vector<uint8_t> kNestedMapTestCase = {
0xa2, // map of 2 pairs
0x61, 0x61, // "a"
0x01,
0x61, 0x62, // "b"
0xa2, // map of 2 pairs
0x61, 0x63, // "c"
0x02,
0x61, 0x64, // "d"
0x03,
};
std::optional<CBORValue> cbor = CBORReader::read(kNestedMapTestCase);
ASSERT_TRUE(cbor.has_value());
const CBORValue cborVal = WTFMove(cbor.value());
ASSERT_TRUE(cborVal.type() == CBORValue::Type::Map);
ASSERT_EQ(cborVal.getMap().size(), 2u);
const CBORValue keyA("a");
ASSERT_EQ(cborVal.getMap().count(keyA), 1u);
ASSERT_TRUE(cborVal.getMap().find(keyA)->second.type() == CBORValue::Type::Unsigned);
EXPECT_EQ(cborVal.getMap().find(keyA)->second.getInteger(), 1u);
const CBORValue keyB("b");
ASSERT_EQ(cborVal.getMap().count(keyB), 1u);
const CBORValue nestedMap = cborVal.getMap().find(keyB)->second.clone();
ASSERT_TRUE(nestedMap.type() == CBORValue::Type::Map);
ASSERT_EQ(nestedMap.getMap().size(), 2u);
const CBORValue keyC("c");
ASSERT_EQ(nestedMap.getMap().count(keyC), 1u);
ASSERT_TRUE(nestedMap.getMap().find(keyC)->second.type() == CBORValue::Type::Unsigned);
EXPECT_EQ(nestedMap.getMap().find(keyC)->second.getInteger(), 2u);
const CBORValue keyD("d");
ASSERT_EQ(nestedMap.getMap().count(keyD), 1u);
ASSERT_TRUE(nestedMap.getMap().find(keyD)->second.type() == CBORValue::Type::Unsigned);
EXPECT_EQ(nestedMap.getMap().find(keyD)->second.getInteger(), 3u);
}
TEST(CBORReaderTest, TestIntegerRange)
{
static const Vector<uint8_t> kMaxPositiveInt = {
0x1b, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
};
static const Vector<uint8_t> kMinNegativeInt = {
0x3b, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
};
std::optional<CBORValue> maxPositiveInt = CBORReader::read(kMaxPositiveInt);
ASSERT_TRUE(maxPositiveInt.has_value());
EXPECT_EQ(maxPositiveInt.value().getInteger(), INT64_MAX);
std::optional<CBORValue> minNegativeInt = CBORReader::read(kMinNegativeInt);
ASSERT_TRUE(minNegativeInt.has_value());
EXPECT_EQ(minNegativeInt.value().getInteger(), INT64_MIN);
}
TEST(CBORReaderTest, TestIntegerOutOfRangeError)
{
static const Vector<uint8_t> kOutOfRangePositiveInt = {
0x1b, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
static const Vector<uint8_t> kOutOfRangeNegativeInt = {
0x3b, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
CBORReader::DecoderError errorCode;
std::optional<CBORValue> positiveIntOutOfRangeCBOR = CBORReader::read(kOutOfRangePositiveInt, &errorCode);
EXPECT_FALSE(positiveIntOutOfRangeCBOR);
EXPECT_TRUE(errorCode == CBORReader::DecoderError::OutOfRangeIntegerValue);
std::optional<CBORValue> negativeIntOutOfRangeCBOR = CBORReader::read(kOutOfRangeNegativeInt, &errorCode);
EXPECT_FALSE(negativeIntOutOfRangeCBOR);
EXPECT_TRUE(errorCode == CBORReader::DecoderError::OutOfRangeIntegerValue);
}
TEST(CBORReaderTest, TestReadSimpleValue)
{
static const struct {
const CBORValue::SimpleValue value;
const Vector<uint8_t> cborData;
} kSimpleValueTestCases[] = {
{ CBORValue::SimpleValue::FalseValue, { 0xf4 } },
{ CBORValue::SimpleValue::TrueValue, { 0xf5 } },
{ CBORValue::SimpleValue::NullValue, { 0xf6 } },
{ CBORValue::SimpleValue::Undefined, { 0xf7 } },
};
for (const auto& testCase : kSimpleValueTestCases) {
std::optional<CBORValue> cbor = CBORReader::read(testCase.cborData);
ASSERT_TRUE(cbor.has_value());
ASSERT_TRUE(cbor.value().type() == CBORValue::Type::SimpleValue);
ASSERT_TRUE(cbor.value().getSimpleValue() == testCase.value);
}
}
TEST(CBORReaderTest, TestReadUnsupportedFloatingPointNumbers)
{
static const Vector<uint8_t> floatingPointCbors[] = {
// 16 bit floating point value.
{ 0xf9, 0x10, 0x00 },
// 32 bit floating point value.
{ 0xfa, 0x10, 0x00, 0x00, 0x00 },
// 64 bit floating point value.
{ 0xfb, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }
};
for (const auto& unsupported_floating_point : floatingPointCbors) {
CBORReader::DecoderError errorCode;
std::optional<CBORValue> cbor = CBORReader::read(unsupported_floating_point, &errorCode);
EXPECT_FALSE(cbor.has_value());
EXPECT_TRUE(errorCode == CBORReader::DecoderError::UnsupportedFloatingPointValue);
}
}
TEST(CBORReaderTest, TestIncompleteCBORDataError)
{
static const Vector<uint8_t> incompleteCborList[] = {
// Additional info byte corresponds to unsigned int that corresponds
// to 2 additional bytes. But actual data encoded in one byte.
{ 0x19, 0x03 },
// CBOR bytestring of length 3 encoded with additional info of length 4.
{ 0x44, 0x01, 0x02, 0x03 },
// CBOR string data "IETF" of length 4 encoded with additional info of
// length 5.
{ 0x65, 0x49, 0x45, 0x54, 0x46 },
// CBOR array of length 1 encoded with additional info of length 2.
{ 0x82, 0x02 },
// CBOR map with single key value pair encoded with additional info of
// length 2.
{ 0xa2, 0x61, 0x61, 0x01 },
};
for (const auto& incomplete_data : incompleteCborList) {
CBORReader::DecoderError errorCode;
std::optional<CBORValue> cbor = CBORReader::read(incomplete_data, &errorCode);
EXPECT_FALSE(cbor.has_value());
EXPECT_TRUE(errorCode == CBORReader::DecoderError::IncompleteCBORData);
}
}
// While RFC 7049 allows CBOR map keys with all types, current decoder only
// supports unsigned integer and string keys.
TEST(CBORReaderTest, TestUnsupportedMapKeyFormatError)
{
static const Vector<uint8_t> kMapWithUintKey = {
0xa2, // map of 2 pairs
0x82, 0x01, 0x02, // invalid key : [1, 2]
0x02, // value : 2
0x61, 0x64, // key : "d"
0x03, // value : 3
};
CBORReader::DecoderError errorCode;
std::optional<CBORValue> cbor = CBORReader::read(kMapWithUintKey, &errorCode);
EXPECT_FALSE(cbor.has_value());
EXPECT_TRUE(errorCode == CBORReader::DecoderError::IncorrectMapKeyType);
}
TEST(CBORReaderTest, TestUnknownAdditionalInfoError)
{
static const Vector<uint8_t> kUnknownAdditionalInfoList[] = {
// "IETF" encoded with major type 3 and additional info of 28.
{ 0x7C, 0x49, 0x45, 0x54, 0x46 },
// "\"\\" encoded with major type 3 and additional info of 29.
{ 0x7D, 0x22, 0x5c },
// "\xc3\xbc" encoded with major type 3 and additional info of 30.
{ 0x7E, 0xc3, 0xbc },
// "\xe6\xb0\xb4" encoded with major type 3 and additional info of 31.
{ 0x7F, 0xe6, 0xb0, 0xb4 },
// Major type 7, additional information 28: unassigned.
{ 0xFC },
// Major type 7, additional information 29: unassigned.
{ 0xFD },
// Major type 7, additional information 30: unassigned.
{ 0xFE },
// Major type 7, additional information 31: "break" stop code for
// indefinite-length items.
{ 0xFF },
};
for (const auto& incorrect_cbor : kUnknownAdditionalInfoList) {
CBORReader::DecoderError errorCode;
std::optional<CBORValue> cbor = CBORReader::read(incorrect_cbor, &errorCode);
EXPECT_FALSE(cbor.has_value());
EXPECT_TRUE(errorCode == CBORReader::DecoderError::UnknownAdditionalInfo);
}
}
TEST(CBORReaderTest, TestTooMuchNestingError)
{
static const Vector<uint8_t> kZeroDepthCBORList[] = {
// Unsigned int with value 100.
{ 0x18, 0x64 },
// CBOR bytestring of length 4.
{ 0x44, 0x01, 0x02, 0x03, 0x04 },
// CBOR string of corresponding to "IETF.
{ 0x64, 0x49, 0x45, 0x54, 0x46 },
// Empty CBOR array.
{ 0x80 },
// Empty CBOR Map
{ 0xa0 },
};
for (const auto& zeroDepthData : kZeroDepthCBORList) {
CBORReader::DecoderError errorCode;
std::optional<CBORValue> cbor = CBORReader::read(zeroDepthData, &errorCode, 0);
EXPECT_TRUE(cbor.has_value());
EXPECT_TRUE(errorCode == CBORReader::DecoderError::CBORNoError);
}
// Corresponds to a CBOR structure with a nesting depth of 2:
// {"a": 1,
// "b": [2, 3]}
static const Vector<uint8_t> kNestedCBORData = {
0xa2, // map of 2 pairs
0x61, 0x61, // "a"
0x01,
0x61, 0x62, // "b"
0x82, // array with 2 elements
0x02,
0x03,
};
CBORReader::DecoderError errorCode;
std::optional<CBORValue> cborSingleLayerMax = CBORReader::read(kNestedCBORData, &errorCode, 1);
EXPECT_FALSE(cborSingleLayerMax.has_value());
EXPECT_TRUE(errorCode == CBORReader::DecoderError::TooMuchNesting);
std::optional<CBORValue> cborDoubleLayerMax = CBORReader::read(kNestedCBORData, &errorCode, 2);
EXPECT_TRUE(cborDoubleLayerMax.has_value());
EXPECT_TRUE(errorCode == CBORReader::DecoderError::CBORNoError);
}
TEST(CBORReaderTest, TestOutOfOrderKeyError)
{
static const Vector<uint8_t> kMapsWithUnsortedKeys[] = {
{0xa2, // map with 2 keys with same major type and length
0x61, 0x62, // key "b"
0x61, 0x42, // value :"B"
0x61, 0x61, // key "a" (out of order byte-wise lexically)
0x61, 0x45, // value "E"
},
{0xa2, // map with 2 keys with different major type
0x61, 0x62, // key "b"
0x02, // value 2
// key 1000 (out of order since lower major type sorts first)
0x19, 0x03, 0xe8,
0x61, 0x61, // value a
},
{0xa2, // map with 2 keys with same major type
0x19, 0x03, 0xe8, // key 1000 (out of order due to longer length)
0x61, 0x61, // value "a"
0x0a, // key 10
0x61, 0x62}, // value "b"
};
CBORReader::DecoderError errorCode;
for (const auto& unsortedMap : kMapsWithUnsortedKeys) {
std::optional<CBORValue> cbor =
CBORReader::read(unsortedMap, &errorCode);
EXPECT_FALSE(cbor.has_value());
EXPECT_TRUE(errorCode == CBORReader::DecoderError::OutOfOrderKey);
}
}
TEST(CBORReaderTest, TestDuplicateKeyError)
{
static const Vector<uint8_t> kMapWithDuplicateKey = {
0xa6, // map of 6 pairs:
0x60, // ""
0x61, 0x2e, // "."
0x61, 0x62, // "b"
0x61, 0x42, // "B"
0x61, 0x62, // "b" (Duplicate key)
0x61, 0x43, // "C"
0x61, 0x64, // "d"
0x61, 0x44, // "D"
0x61, 0x65, // "e"
0x61, 0x44, // "D"
0x62, 0x61, 0x61, // "aa"
0x62, 0x41, 0x41, // "AA"
};
CBORReader::DecoderError errorCode;
std::optional<CBORValue> cbor = CBORReader::read(kMapWithDuplicateKey, &errorCode);
EXPECT_FALSE(cbor.has_value());
EXPECT_TRUE(errorCode == CBORReader::DecoderError::DuplicateKey);
}
// Leveraging Markus Kuhn’s UTF-8 decoder stress test. See
// http://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-test.txt for details.
TEST(CBORReaderTest, TestIncorrectStringEncodingError)
{
static const Vector<uint8_t> utf8CharacterEncodings[] = {
// Corresponds to utf8 encoding of "퟿" (section 2.3.1 of stress test).
{ 0x63, 0xED, 0x9F, 0xBF },
// Corresponds to utf8 encoding of "" (section 2.3.2 of stress test).
{ 0x63, 0xEE, 0x80, 0x80 },
// Corresponds to utf8 encoding of "�" (section 2.3.3 of stress test).
{ 0x63, 0xEF, 0xBF, 0xBD },
};
CBORReader::DecoderError errorCode;
for (const auto& cbor_byte : utf8CharacterEncodings) {
std::optional<CBORValue> correctlyEncodedCbor = CBORReader::read(cbor_byte, &errorCode);
EXPECT_TRUE(correctlyEncodedCbor.has_value());
EXPECT_TRUE(errorCode == CBORReader::DecoderError::CBORNoError);
}
// Incorrect UTF8 encoding referenced by section 3.5.3 of the stress test.
Vector<uint8_t> impossible_utf_byte { 0x64, 0xfe, 0xfe, 0xff, 0xff };
std::optional<CBORValue> incorrectlyEncodedCbor = CBORReader::read(impossible_utf_byte, &errorCode);
EXPECT_FALSE(incorrectlyEncodedCbor);
EXPECT_TRUE(errorCode == CBORReader::DecoderError::InvalidUTF8);
}
TEST(CBORReaderTest, TestExtraneousCBORDataError)
{
static const Vector<uint8_t> zeroPaddedCborList[] = {
// 1 extra byte after a 2-byte unsigned int.
{ 0x19, 0x03, 0x05, 0x00 },
// 1 extra byte after a 4-byte cbor byte array.
{ 0x44, 0x01, 0x02, 0x03, 0x04, 0x00 },
// 1 extra byte after a 4-byte string.
{ 0x64, 0x49, 0x45, 0x54, 0x46, 0x00 },
// 1 extra byte after CBOR array of length 2.
{ 0x82, 0x01, 0x02, 0x00 },
// 1 extra key value pair after CBOR map of size 2.
{ 0xa1, 0x61, 0x63, 0x02, 0x61, 0x64, 0x03 },
};
for (const auto& extraneous_cborData : zeroPaddedCborList) {
CBORReader::DecoderError errorCode;
std::optional<CBORValue> cbor = CBORReader::read(extraneous_cborData, &errorCode);
EXPECT_FALSE(cbor.has_value());
EXPECT_TRUE(errorCode == CBORReader::DecoderError::ExtraneousData);
}
}
TEST(CBORReaderTest, TestUnsupportedSimplevalue)
{
static const Vector<uint8_t> unsupportedSimpleValues[] = {
// Simple value (0, unassigned)
{ 0xE0 },
// Simple value (19, unassigned)
{ 0xF3 },
// Simple value (24, reserved)
{ 0xF8, 0x18 },
// Simple value (28, reserved)
{ 0xF8, 0x1C },
// Simple value (29, reserved)
{ 0xF8, 0x1D },
// Simple value (30, reserved)
{ 0xF8, 0x1E },
// Simple value (31, reserved)
{ 0xF8, 0x1F },
// Simple value (32, unassigned)
{ 0xF8, 0x20 },
// Simple value (255, unassigned)
{ 0xF8, 0xFF },
};
for (const auto& unsupportedSimpleVal : unsupportedSimpleValues) {
CBORReader::DecoderError errorCode;
std::optional<CBORValue> cbor = CBORReader::read(unsupportedSimpleVal, &errorCode);
EXPECT_FALSE(cbor.has_value());
EXPECT_TRUE(errorCode == CBORReader::DecoderError::UnsupportedSimpleValue);
}
}
} // namespace TestWebKitAPI
#endif // ENABLE(WEB_AUTHN)