| // 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/utils.h" | |
| #include <sys/types.h> | |
| #include <cstdint> | |
| #include <utility> | |
| #include <variant> | |
| #include "absl/functional/overload.h" | |
| #include "absl/status/status.h" | |
| #include "absl/status/status_macros.h" | |
| #include "absl/status/statusor.h" | |
| #include "absl/strings/cord.h" | |
| #include "absl/strings/string_view.h" | |
| #include "absl/strings/strip.h" | |
| #include "jumbf/box.h" | |
| #include "jumbf/constants.h" | |
| #include "jumbf/internal/encode.h" | |
| #include "jumbf/internal/serialization_utils.h" | |
| #include "riegeli/bytes/cord_backward_writer.h" | |
| #include "uuid/uuid.h" | |
| namespace jumbf { | |
| namespace { | |
| // Consumes the header of a JUMBF Box and returns the TBox. | |
| // | |
| // The header is a 4 byte unsigned integer (LBox) followed by a 4 byte unsigned | |
| // integer (TBox). If the first integer is 1, it's followed by a 8 byte unsigned | |
| // integer (XLBox). | |
| absl::StatusOr<uint32_t> ConsumeHeader(absl::string_view* payload) { | |
| ABSL_ASSIGN_OR_RETURN(auto lbox, | |
| jumbf_internal::ConsumeInteger<uint32_t>(payload)); | |
| ABSL_ASSIGN_OR_RETURN(auto tbox, | |
| jumbf_internal::ConsumeInteger<uint32_t>(payload)); | |
| if (lbox == 1) { | |
| ABSL_RETURN_IF_ERROR( | |
| jumbf_internal::ConsumeBytes(payload, sizeof(uint64_t)).status()); | |
| } | |
| return tbox; | |
| } | |
| } // namespace | |
| absl::StatusOr<absl::string_view> StripBoxHeaders(absl::string_view raw_bytes) { | |
| ABSL_ASSIGN_OR_RETURN(auto lbox, | |
| jumbf_internal::ConsumeInteger<uint32_t>(&raw_bytes)); | |
| ABSL_RETURN_IF_ERROR( | |
| jumbf_internal::ConsumeBytes(&raw_bytes, sizeof(uint32_t)).status()); | |
| if (lbox == 1) { | |
| ABSL_RETURN_IF_ERROR( | |
| jumbf_internal::ConsumeBytes(&raw_bytes, sizeof(uint64_t)).status()); | |
| } | |
| return raw_bytes; | |
| } | |
| absl::StatusOr<bool> HasDescriptionBoxMatching(absl::string_view raw_bytes, | |
| const credentio::Uuid& uuid, | |
| uint8_t toggles_mask, | |
| absl::string_view label) { | |
| // Super Box Header | |
| ABSL_ASSIGN_OR_RETURN(auto super_tbox, ConsumeHeader(&raw_bytes)); | |
| if (super_tbox != kSuperBoxType) { | |
| return false; | |
| } | |
| // Description Box Header | |
| ABSL_ASSIGN_OR_RETURN(auto description_tbox, ConsumeHeader(&raw_bytes)); | |
| if (description_tbox != kDescriptionBoxType) { | |
| return false; | |
| } | |
| // Description Box UUID | |
| if (raw_bytes.size() < 16) { | |
| return absl::OutOfRangeError("Description Box UUID is missing."); | |
| } | |
| ABSL_ASSIGN_OR_RETURN(auto description_uuid, credentio::Uuid::FromProtoBytes( | |
| raw_bytes.substr(0, 16))); | |
| raw_bytes.remove_prefix(16); | |
| if (std::move(description_uuid) != uuid) { | |
| return false; | |
| } | |
| // Description Box Toggles | |
| ABSL_ASSIGN_OR_RETURN(auto toggles, | |
| jumbf_internal::ConsumeInteger<uint8_t>(&raw_bytes)); | |
| if ((toggles & toggles_mask) != toggles_mask) { | |
| return false; | |
| } | |
| // Expected Label | |
| if (!absl::ConsumePrefix(&raw_bytes, label)) { | |
| return false; | |
| } | |
| // Ensure the next character is the null terminator | |
| ABSL_ASSIGN_OR_RETURN(auto next_byte, | |
| jumbf_internal::ConsumeInteger<uint8_t>(&raw_bytes)); | |
| return next_byte == 0x00; | |
| } | |
| int GetContextBoxSize(const ContentBox& box) { | |
| int content_size = 0; | |
| std::visit( | |
| absl::Overload{ | |
| [&](const UnknownBox& arg) { content_size = arg.payload.size(); }, | |
| [&](const CborBox& arg) { content_size = arg.payload.size(); }, | |
| [&](const SerializedSuperbox& arg) { | |
| content_size = arg.serialized.size(); | |
| }, | |
| [&](const SuperBox& arg) { content_size = arg.raw_bytes.size(); }, | |
| [&](const EmbeddedFileDescriptionBox& arg) { | |
| // See section B.6.2 | |
| // Always include a byte for toggles | |
| content_size = 1; | |
| // Add media type size + 1 for null terminator | |
| content_size += arg.media_type.size() + 1; | |
| if (arg.file_name.has_value()) { | |
| // Add file name size + 1 for null terminator | |
| content_size += arg.file_name->size() + 1; | |
| } | |
| }, | |
| [&](const BinaryDataBox& arg) { content_size = arg.payload.size(); }, | |
| [&](const C2paSaltBox& arg) { content_size = arg.salt.size(); }, | |
| [&](const JsonBox& arg) { content_size = arg.payload.size(); }, | |
| [&](const UuidBox& arg) { content_size = arg.payload.size(); }, | |
| }, | |
| box.payload); | |
| return content_size; | |
| } | |
| absl::StatusOr<absl::Cord> CreateC2paSaltBox(absl::string_view salt) { | |
| riegeli::CordBackwardWriter<absl::Cord> writer; | |
| ABSL_RETURN_IF_ERROR( | |
| EncodeContentBox(kC2paSaltBoxType, absl::Cord(salt), &writer)); | |
| if (!writer.Close()) { | |
| return writer.status(); | |
| } | |
| return std::move(writer.dest()); | |
| } | |
| } // namespace jumbf |