blob: e239d5c7848f96de73e9c2210bd2523ef75f7fa8 [file] [edit]
// 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 "jumbf/internal/parse_description_box.h"
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <string>
#include "absl/status/status.h"
#include "absl/status/status_matchers.h"
#include "absl/strings/string_view.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "jumbf/constants.h"
#include "jumbf/internal/intermediate.h"
#include "jumbf/test_utils.h"
#include "riegeli/endian/endian_writing.h"
#include "uuid/uuid.h"
namespace jumbf_internal {
namespace {
using ::absl_testing::IsOkAndHolds;
using ::absl_testing::StatusIs;
using ::credentio::Uuid;
using ::jumbf::SerializeUuid;
constexpr absl::string_view kFakeUuid =
R"(deadbeef-f00d-babe-b4df-00d528013378)";
TEST(ParseDescriptionBoxTest, ParseOkNoToggles) {
// Can't use absl::string_view because the compiler warns against assuming it
// is null terminated, even when it truly is null terminated.
const size_t description_content_length = /*uuid*/ 16 + /*toggles*/ 1;
std::string fake_description_contents(description_content_length, 'A');
char* content_cursor = fake_description_contents.data();
SerializeUuid(&content_cursor, Uuid::FromStringOrDie(kFakeUuid));
riegeli::WriteBigEndian<uint8_t>(0, content_cursor);
content_cursor += sizeof(uint8_t);
IntermediateBox unwrapped_description{
.payload = fake_description_contents,
.type = jumbf::kDescriptionBoxType,
};
EXPECT_THAT(ParseDescriptionBox(unwrapped_description),
IsOkAndHolds(IntermediateDescriptionBox{
.type_uuid = Uuid::FromStringOrDie(kFakeUuid)}));
}
TEST(ParseDescriptionBoxTest, InvalidPayloadSize) {
// A fuzzer crafted this box, with a payload that is too short.
IntermediateBox box = {
.payload = "",
.serialized = absl::string_view("\x00\x00\x00\x08jumd", 8),
.type = 1786080612,
.implicit_length = false};
EXPECT_THAT(ParseDescriptionBox(box),
StatusIs(absl::StatusCode::kOutOfRange));
}
TEST(ParseDescriptionBoxTest, ParseOkAllToggles) {
// Can't use absl::string_view because the compiler warns against assuming it
// is null terminated, even when it truly is null terminated.
constexpr char kFakeLabel[] = "fakelabel";
constexpr uint32_t kFakeId = 0xB33FD00D;
const std::string fake_hash(32, 'X');
constexpr absl::string_view kPrivatePayload =
R"(The format of this data is unimportant for this test case.)";
const std::string private_box = jumbf::WrapBox(kPrivatePayload, 12345);
const size_t description_content_length =
/*uuid*/ 16 + /*toggles*/ 1 + strlen(kFakeLabel) +
/*label null terminator*/ 1 + /*id*/ 4 + fake_hash.length() +
private_box.length();
std::string fake_description_contents(description_content_length, 'A');
char* content_cursor = fake_description_contents.data();
SerializeUuid(&content_cursor, Uuid::FromStringOrDie(kFakeUuid));
uint8_t toggles = jumbf::kDescriptionToggleRequestable |
jumbf::kDescriptionToggleLabelPresent |
jumbf::kDescriptionToggleIdPresent |
jumbf::kDescriptionToggleHashPresent |
jumbf::kDescriptionTogglePrivatePresent;
riegeli::WriteBigEndian<uint8_t>(toggles, content_cursor);
content_cursor += sizeof(uint8_t);
memcpy(content_cursor, kFakeLabel, strlen(kFakeLabel) + 1);
content_cursor += strlen(kFakeLabel) + 1;
riegeli::WriteBigEndian<uint32_t>(kFakeId, content_cursor);
content_cursor += sizeof(uint32_t);
memcpy(content_cursor, fake_hash.data(), 32);
content_cursor += 32;
memcpy(content_cursor, private_box.data(), private_box.length());
IntermediateBox unwrapped_description{
.payload = fake_description_contents,
.type = jumbf::kDescriptionBoxType,
};
EXPECT_THAT(ParseDescriptionBox(unwrapped_description),
IsOkAndHolds(IntermediateDescriptionBox{
.type_uuid = Uuid::FromStringOrDie(kFakeUuid),
.requestable = true,
.label = kFakeLabel,
.id = kFakeId,
.hash = fake_hash,
.private_box =
IntermediateBox{
.payload = kPrivatePayload,
.serialized = private_box,
.type = 12345,
},
}));
}
TEST(ParseDescriptionBoxTest, RequestableNoLabelFails) {
const size_t description_content_length = /*uuid*/ 16 + /*toggles*/ 1;
std::string fake_description_contents(description_content_length, 'A');
char* content_cursor = fake_description_contents.data();
SerializeUuid(&content_cursor, Uuid::FromStringOrDie(kFakeUuid));
riegeli::WriteBigEndian<uint8_t>(jumbf::kDescriptionToggleRequestable,
content_cursor);
content_cursor += sizeof(uint8_t);
IntermediateBox unwrapped_description{
.payload = fake_description_contents,
.type = jumbf::kDescriptionBoxType,
};
EXPECT_THAT(ParseDescriptionBox(unwrapped_description),
StatusIs(absl::StatusCode::kInvalidArgument,
"requestable bit is set, but box does not have a label; "
"if a box is requestable the label is required"));
}
TEST(ParseDescriptionBoxTest, ParseOkWithLabel) {
// Can't use absl::string_view because the compiler warns against assuming it
// is null terminated, even when it truly is null terminated.
constexpr char kFakeLabel[] = "fakelabel";
const size_t description_content_length = /*uuid*/ 16 + /*toggles*/ 1 +
strlen(kFakeLabel) +
/*label null terminator*/ 1;
std::string fake_description_contents(description_content_length, 'A');
char* content_cursor = fake_description_contents.data();
SerializeUuid(&content_cursor, Uuid::FromStringOrDie(kFakeUuid));
riegeli::WriteBigEndian<uint8_t>(jumbf::kDescriptionToggleLabelPresent,
content_cursor);
content_cursor += sizeof(uint8_t);
memcpy(content_cursor, kFakeLabel, strlen(kFakeLabel) + 1);
content_cursor += strlen(kFakeLabel) + 1;
IntermediateBox unwrapped_description{
.payload = fake_description_contents,
.type = jumbf::kDescriptionBoxType,
};
EXPECT_THAT(
ParseDescriptionBox(unwrapped_description),
IsOkAndHolds(IntermediateDescriptionBox{
.type_uuid = Uuid::FromStringOrDie(kFakeUuid), .label = kFakeLabel}));
}
TEST(ParseDescriptionBoxTest, ParseOkWithId) {
constexpr uint32_t kFakeId = 0xB33FD00D;
const size_t description_content_length =
/*uuid*/ 16 + /*toggles*/ 1 + /*id*/ 4;
std::string fake_description_contents(description_content_length, 'A');
char* content_cursor = fake_description_contents.data();
SerializeUuid(&content_cursor, Uuid::FromStringOrDie(kFakeUuid));
riegeli::WriteBigEndian<uint8_t>(jumbf::kDescriptionToggleIdPresent,
content_cursor);
content_cursor += sizeof(uint8_t);
riegeli::WriteBigEndian<uint32_t>(kFakeId, content_cursor);
content_cursor += sizeof(uint32_t);
IntermediateBox unwrapped_description{
.payload = fake_description_contents,
.type = jumbf::kDescriptionBoxType,
};
EXPECT_THAT(
ParseDescriptionBox(unwrapped_description),
IsOkAndHolds(IntermediateDescriptionBox{
.type_uuid = Uuid::FromStringOrDie(kFakeUuid), .id = kFakeId}));
}
TEST(ParseDescriptionBoxTest, ParseOkWithHash) {
const std::string fake_hash(32, 'X');
const size_t description_content_length =
/*uuid*/ 16 + /*toggles*/ 1 + fake_hash.length();
std::string fake_description_contents(description_content_length, 'A');
char* content_cursor = fake_description_contents.data();
SerializeUuid(&content_cursor, Uuid::FromStringOrDie(kFakeUuid));
riegeli::WriteBigEndian<uint8_t>(jumbf::kDescriptionToggleHashPresent,
content_cursor);
content_cursor += sizeof(uint8_t);
memcpy(content_cursor, fake_hash.data(), 32);
IntermediateBox unwrapped_description{
.payload = fake_description_contents,
.type = jumbf::kDescriptionBoxType,
};
EXPECT_THAT(
ParseDescriptionBox(unwrapped_description),
IsOkAndHolds(IntermediateDescriptionBox{
.type_uuid = Uuid::FromStringOrDie(kFakeUuid), .hash = fake_hash}));
}
TEST(ParseDescriptionBoxTest, ParseOkWithPrivate) {
constexpr absl::string_view kPrivatePayload =
R"(The format of this data is unimportant for this test case.)";
const std::string private_box = jumbf::WrapBox(kPrivatePayload, 12345);
const size_t description_content_length =
/*uuid*/ 16 + /*toggles*/ 1 + private_box.length();
std::string fake_description_contents(description_content_length, 'A');
char* content_cursor = fake_description_contents.data();
SerializeUuid(&content_cursor, Uuid::FromStringOrDie(kFakeUuid));
riegeli::WriteBigEndian<uint8_t>(jumbf::kDescriptionTogglePrivatePresent,
content_cursor);
content_cursor += sizeof(uint8_t);
memcpy(content_cursor, private_box.data(), private_box.length());
IntermediateBox unwrapped_description{
.payload = fake_description_contents,
.type = jumbf::kDescriptionBoxType,
};
EXPECT_THAT(ParseDescriptionBox(unwrapped_description),
IsOkAndHolds(IntermediateDescriptionBox{
.type_uuid = Uuid::FromStringOrDie(kFakeUuid),
.private_box =
IntermediateBox{
.payload = kPrivatePayload,
.serialized = private_box,
.type = 12345,
},
}));
}
TEST(ParseDescriptionBoxTest, WrongTypeFails) {
// Can't use absl::string_view because the compiler warns against assuming it
// is null terminated, even when it truly is null terminated.
const size_t description_content_length = /*uuid*/ 16 + /*toggles*/ 1;
std::string fake_description_contents(description_content_length, 'A');
char* content_cursor = fake_description_contents.data();
SerializeUuid(&content_cursor, Uuid::FromStringOrDie(kFakeUuid));
riegeli::WriteBigEndian<uint8_t>(0, content_cursor);
content_cursor += sizeof(uint8_t);
IntermediateBox unwrapped_description{
.payload = fake_description_contents,
.type = jumbf::kSuperBoxType,
};
EXPECT_THAT(ParseDescriptionBox(unwrapped_description),
StatusIs(absl::StatusCode::kInvalidArgument,
"box is not a description box; got type 0x6a756d62 need "
"0x6a756d64 ('jumd')"));
}
TEST(ParseDescriptionBoxTest, UnterminatedLabelFails) {
// Can't use absl::string_view because the compiler warns against assuming it
// is null terminated, even when it truly is null terminated.
constexpr char kFakeLabel[] = "fakelabel";
const size_t description_content_length =
/*uuid*/ 16 + /*toggles*/ 1 + strlen(kFakeLabel);
std::string fake_description_contents(description_content_length, 'A');
char* content_cursor = fake_description_contents.data();
SerializeUuid(&content_cursor, Uuid::FromStringOrDie(kFakeUuid));
riegeli::WriteBigEndian<uint8_t>(jumbf::kDescriptionToggleLabelPresent,
content_cursor);
content_cursor += sizeof(uint8_t);
// Intentionally not copying null terminator.
memcpy(content_cursor, kFakeLabel, strlen(kFakeLabel));
IntermediateBox unwrapped_description{
.payload = fake_description_contents,
.type = jumbf::kDescriptionBoxType,
};
EXPECT_THAT(ParseDescriptionBox(unwrapped_description),
StatusIs(absl::StatusCode::kOutOfRange,
"cannot read label: ran out of input bytes before "
"reading null terminator"));
}
} // namespace
} // namespace jumbf_internal