| // 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/string_view.h" |
| #include "absl/strings/strip.h" |
| #include "jumbf/box.h" |
| #include "jumbf/constants.h" |
| #include "jumbf/internal/serialization_utils.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; |
| } |
| |
| } // namespace jumbf |