blob: 629b1a65b1edd43878ae4d155fe3a10184238e04 [file]
// 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