| // 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 "claim/manifest_store_builder.h" | |
| #include <string> | |
| #include <utility> | |
| #include <vector> | |
| #include "absl/status/status.h" | |
| #include "absl/status/status_macros.h" | |
| #include "absl/status/statusor.h" | |
| #include "absl/strings/cord.h" | |
| #include "absl/strings/str_cat.h" | |
| #include "absl/strings/string_view.h" | |
| #include "claim/manifest_store_view.h" | |
| #include "constants/labels.h" | |
| #include "crypto/hash.h" | |
| #include "jumbf/box.h" | |
| #include "jumbf/box_builder.h" | |
| #include "jumbf/encode_params.h" | |
| #include "jumbf/utils.h" | |
| #include "proto/hashed_uri.pb.h" | |
| namespace credentio { | |
| namespace { | |
| bool IsManifestBox(const jumbf::SuperBox& super_box) { | |
| return super_box.description.type_uuid == kStandardManifestUuid || | |
| super_box.description.type_uuid == kCompressedManifestUuid || | |
| super_box.description.type_uuid == kUpdateManifestUuid || | |
| super_box.description.type_uuid == kTimestampManifestUuid; | |
| } | |
| absl::StatusOr<HashedUri> CreateHashedUriForBox( | |
| absl::string_view url, absl::string_view box_raw_bytes, | |
| const HasherFactory& hasher_factory) { | |
| ABSL_ASSIGN_OR_RETURN(absl::string_view content, | |
| jumbf::StripBoxHeaders(box_raw_bytes)); | |
| ABSL_ASSIGN_OR_RETURN(auto hasher, hasher_factory.Create()); | |
| hasher->Update(content); | |
| HashedUri hashed_uri; | |
| hashed_uri.set_url(url); | |
| hashed_uri.set_hash(hasher->Digest()); | |
| return hashed_uri; | |
| } | |
| } // namespace | |
| absl::StatusOr<IngredientManifestUris> | |
| ManifestStoreBuilder::AddIngredientManifestStore( | |
| absl::string_view manifest_store_bytes) { | |
| ABSL_ASSIGN_OR_RETURN(auto manifest_store_view, | |
| ManifestStoreView::Create(manifest_store_bytes)); | |
| ABSL_ASSIGN_OR_RETURN(auto active_manifest_view, | |
| manifest_store_view.GetActiveManifest()); | |
| std::string active_manifest_url = | |
| absl::StrCat(kManifestStoreUrlPrefix, active_manifest_view.label()); | |
| IngredientManifestUris ingredient_manifest_uris; | |
| ABSL_ASSIGN_OR_RETURN( | |
| ingredient_manifest_uris.active_manifest, | |
| CreateHashedUriForBox(active_manifest_url, | |
| active_manifest_view.superbox().raw_bytes, | |
| hasher_factory_)); | |
| ABSL_ASSIGN_OR_RETURN(auto claim_signature_view, | |
| active_manifest_view.GetClaimSignature()); | |
| ABSL_ASSIGN_OR_RETURN( | |
| ingredient_manifest_uris.claim_signature, | |
| CreateHashedUriForBox( | |
| absl::StrCat(active_manifest_url, kManifestLabelDelimiter, | |
| kClaimSignatureLabel), | |
| claim_signature_view.superbox().raw_bytes, hasher_factory_)); | |
| ingredient_manifest_stores_.push_back(manifest_store_view.superbox()); | |
| return ingredient_manifest_uris; | |
| } | |
| absl::StatusOr<jumbf::BuiltSuperBox> ManifestStoreBuilder::Finalize( | |
| jumbf::BuiltSuperBox&& active_manifest) && { | |
| std::vector<absl::Cord> ingredient_manifests; | |
| for (const jumbf::SuperBox& manifest_store : ingredient_manifest_stores_) { | |
| for (const jumbf::ContentBox& box : manifest_store.contents) { | |
| if (!box.Holds<jumbf::SuperBox>()) { | |
| continue; | |
| } | |
| const jumbf::SuperBox& super_box = box.Get<jumbf::SuperBox>(); | |
| if (!IsManifestBox(super_box)) { | |
| continue; | |
| } | |
| if (!super_box.description.label.has_value()) { | |
| continue; | |
| } | |
| ManifestInfo& manifest_info = | |
| manifest_info_[*super_box.description.label]; | |
| if (manifest_info.copied_to_manifest_store) { | |
| continue; | |
| } | |
| ingredient_manifests.emplace_back(super_box.raw_bytes); | |
| manifest_info.copied_to_manifest_store = true; | |
| } | |
| } | |
| if (!active_manifest.description_label().has_value()) { | |
| return absl::InvalidArgumentError("active manifest label is missing"); | |
| } | |
| if (manifest_info_[*active_manifest.description_label()] | |
| .copied_to_manifest_store) { | |
| // Shouldn't happen since manifest labels are generated randomly and | |
| // the active manifest can't be an ingredient of itself. | |
| return absl::InternalError( | |
| "active manifest label is also an ingredient manifest label"); | |
| } | |
| jumbf::SuperBoxBuilder manifest_store_builder( | |
| kManifestStoreUuid, | |
| jumbf::DescriptionBoxOptions{.label = absl::Cord(kManifestStoreLabel)}); | |
| // SuperBoxBuilder reverses the order of boxes added via AddChild when | |
| // finalized. We iterate in reverse order to preserve the order of the | |
| // ingredient manifests in the new manifest store. | |
| ABSL_RETURN_IF_ERROR( | |
| manifest_store_builder.AddChild(std::move(active_manifest))); | |
| for (int i = ingredient_manifests.size() - 1; i >= 0; --i) { | |
| ABSL_RETURN_IF_ERROR( | |
| manifest_store_builder.AddChild(std::move(ingredient_manifests[i]))); | |
| } | |
| return std::move(manifest_store_builder).Finalize(); | |
| } | |
| } // namespace credentio |