| // 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 "testing/jumbf_utils.h" |
| |
| #include <cstdint> |
| #include <memory> |
| #include <optional> |
| #include <string> |
| #include <vector> |
| |
| #include "absl/log/absl_check.h" |
| #include "absl/log/check.h" |
| #include "absl/status/status.h" |
| #include "absl/status/status_macros.h" |
| #include "absl/status/status_matchers.h" // IWYU pragma: keep |
| #include "absl/strings/string_view.h" |
| #include "absl/types/span.h" |
| #include "assertion/assertion_encoder.h" |
| #include "constants/labels.h" |
| #include "crypto/default/hasher.h" |
| #include "crypto/hash.h" |
| #include "jumbf/box.h" |
| #include "jumbf/constants.h" |
| #include "jumbf/parse.h" |
| #include "jumbf/test_utils.h" |
| #include "proto/assertion.pb.h" |
| #include "utils/byte_readers.h" |
| #include "utils/byte_writers.h" |
| #include "uuid/uuid.h" |
| |
| namespace credentio { |
| namespace { |
| |
| absl::StatusOr<absl::string_view> TrimJumbfHeader(absl::string_view raw_bytes) { |
| ABSL_ASSIGN_OR_RETURN(auto lbox, ConsumeUint<uint32_t>(&raw_bytes)); |
| ABSL_RETURN_IF_ERROR(SkipBytes<uint32_t>(&raw_bytes)); |
| if (lbox == 1) { |
| auto xlbox_status = SkipBytes<uint64_t>(&raw_bytes); |
| ABSL_RETURN_IF_ERROR(xlbox_status); |
| } |
| return raw_bytes; |
| } |
| |
| std::string ComputeHashOrDie(absl::string_view algorithm, |
| absl::string_view contents) { |
| auto hasher = CreateHasher(algorithm); |
| ABSL_CHECK_OK(hasher) << "Unsupported algorithm: " << algorithm; |
| (*hasher)->Update(contents); |
| return (*hasher)->Digest(); |
| } |
| |
| } // namespace |
| |
| jumbf::SuperBox ParseSuperBoxOrDie(const std::string* bytes) { |
| absl::string_view bytes_view = *bytes; |
| auto box = jumbf::ConsumeSuperBox(&bytes_view, /*recursion_limit=*/-1); |
| ABSL_CHECK_OK(box); |
| ABSL_CHECK_EQ(bytes_view, ""); |
| return *box; |
| } |
| |
| jumbf::SuperBox CreateManifestStore( |
| absl::Span<const jumbf::SuperBox> manifests) { |
| jumbf::SuperBox box = { |
| .description = |
| { |
| .type_uuid = kManifestStoreUuid, |
| .label = kManifestStoreLabel, |
| }, |
| .raw_bytes = "b64'testing'", |
| }; |
| for (const auto& m : manifests) { |
| box.contents.push_back({.payload = m}); |
| } |
| return box; |
| } |
| |
| jumbf::SuperBox CreateStandardManifest( |
| absl::string_view label, absl::Span<const jumbf::SuperBox> boxes) { |
| jumbf::SuperBox box = { |
| .description = |
| { |
| .type_uuid = kStandardManifestUuid, |
| .label = label, |
| }, |
| .raw_bytes = "b64'testing'", |
| }; |
| for (const auto& b : boxes) { |
| box.contents.push_back({.payload = b}); |
| } |
| return box; |
| } |
| |
| jumbf::SuperBox CreateUpdateManifest(absl::string_view label, |
| absl::Span<const jumbf::SuperBox> boxes) { |
| jumbf::SuperBox box = { |
| .description = |
| { |
| .type_uuid = kUpdateManifestUuid, |
| .label = label, |
| }, |
| .raw_bytes = "b64'testing'", |
| }; |
| for (const auto& b : boxes) { |
| box.contents.push_back({.payload = b}); |
| } |
| return box; |
| } |
| |
| std::string EncodeClaimSignatureBox(absl::string_view cbor) { |
| return jumbf::EncodeSuperBox(kClaimSignatureUuid, kClaimSignatureLabel, |
| {jumbf::EncodeCborBox(cbor)}); |
| } |
| |
| std::string EncodeAssertionBoxOrDie(const Assertion& assertion, |
| const AssertionEncoder& encoder) { |
| auto assertion_cbor = encoder.ToCbor(assertion); |
| ABSL_CHECK_OK(assertion_cbor); |
| std::string assertion_cbor_box = |
| jumbf::EncodeCborBox(std::string(*assertion_cbor)); |
| return jumbf::EncodeSuperBox(jumbf::kCborBoxTypeUuid, assertion.label(), |
| {assertion_cbor_box}, /*requestable=*/true); |
| } |
| |
| jumbf::SuperBox CreateAssertionStoreBox() { |
| return { |
| .description = |
| { |
| .type_uuid = kAssertionStoreUuid, |
| .label = kAssertionStoreLabel, |
| }, |
| .raw_bytes = "b64'testing'", |
| }; |
| } |
| |
| jumbf::SuperBox CreateAssertionStoreBox( |
| absl::Span<const jumbf::SuperBox> assertions) { |
| auto assertion_store_box = CreateAssertionStoreBox(); |
| for (const auto& assertion : assertions) { |
| assertion_store_box.contents.push_back({.payload = assertion}); |
| } |
| return assertion_store_box; |
| } |
| |
| jumbf::SuperBox CreateAssertionBoxFromCbor(absl::string_view label, |
| absl::string_view cbor) { |
| return { |
| .description = {.label = label}, |
| .contents = {jumbf::ContentBox{.payload = |
| jumbf::CborBox{.payload = cbor}}}, |
| .raw_bytes = "b64'testing'", |
| }; |
| } |
| |
| jumbf::SuperBox CreateAssertionBoxFromJson(absl::string_view label, |
| absl::string_view json) { |
| return { |
| .description = {.label = label}, |
| .contents = {jumbf::ContentBox{.payload = |
| jumbf::JsonBox{.payload = json}}}, |
| .raw_bytes = "b64'testing'", |
| }; |
| } |
| |
| jumbf::SuperBox CreateAssertionBoxFromEmbeddedFile( |
| absl::string_view label, absl::string_view media_type, |
| absl::string_view file_contents, |
| std::optional<absl::string_view> file_name) { |
| return { |
| .description = {.label = label}, |
| .contents = {jumbf::ContentBox{.payload = |
| jumbf::EmbeddedFileDescriptionBox{ |
| .media_type = media_type, |
| .file_name = file_name}}, |
| jumbf::ContentBox{ |
| .payload = |
| jumbf::BinaryDataBox{.payload = file_contents}}}, |
| .raw_bytes = "b64'testing'", |
| }; |
| } |
| |
| jumbf::SuperBox CreateActionsAssertionBoxFromCbor(absl::string_view cbor) { |
| return CreateAssertionBoxFromCbor(kActionsAssertionV2Label, cbor); |
| } |
| |
| jumbf::SuperBox CreateBmffBasedHashAssertionBoxFromCbor( |
| absl::string_view cbor) { |
| return CreateAssertionBoxFromCbor(kBmffBasedHashAssertionV3Label, cbor); |
| } |
| |
| jumbf::SuperBox CreateDataHashAssertionBoxFromCbor(absl::string_view cbor) { |
| return CreateAssertionBoxFromCbor(kDataHashAssertionLabel, cbor); |
| } |
| |
| std::string GetHash(absl::string_view algorithm, const jumbf::SuperBox& box) { |
| auto raw_bytes = TrimJumbfHeader(box.raw_bytes); |
| ABSL_CHECK_OK(raw_bytes); |
| return ComputeHashOrDie(algorithm, *raw_bytes); |
| } |
| |
| std::string CreateStartOfManifestStorePayload( |
| absl::Span<const uint8_t> prefix) { |
| std::vector<uint8_t> bytes; |
| bytes.reserve(prefix.size() + 40); |
| for (const auto& byte : prefix) { |
| bytes.push_back(byte); |
| } |
| WriteUint32NetworkOrder(10, &bytes); |
| WriteUint32NetworkOrder(jumbf::kSuperBoxType, &bytes); |
| WriteUint32NetworkOrder(10, &bytes); |
| WriteUint32NetworkOrder(jumbf::kDescriptionBoxType, &bytes); |
| WriteUint64NetworkOrder(0x6332'7061'0011'0010, &bytes); |
| WriteUint64NetworkOrder(0x8000'00AA'0038'9B71, &bytes); |
| bytes.push_back(0b0000'0011); |
| WriteUint32NetworkOrder('c2pa', &bytes); |
| bytes.push_back(0x00); |
| return std::string(reinterpret_cast<const char*>(bytes.data()), bytes.size()); |
| } |
| |
| } // namespace credentio |