blob: e3188856b7cd17e8933123a5150f7fdd02cf9120 [file] [edit]
// 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 "bindings/bmff_hash_hard_binding_validator.h"
#include <memory>
#include <utility>
#include "absl/base/nullability.h"
#include "absl/log/die_if_null.h"
#include "absl/status/status.h"
#include "absl/status/status_macros.h"
#include "absl/status/statusor.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h"
#include "bindings/binding_hasher.h"
#include "bindings/bmff_binding_hasher.h"
#include "bindings/bmff_exclusion_checker.h"
#include "bindings/merkle_validator.h"
#include "constants/status_codes.h"
#include "formats/asset_byte_info.h"
#include "proto/bmff_based_hash_assertion.pb.h"
#include "proto/manifest.pb.h"
#include "proto/multi_asset_hash_assertion.pb.h"
#include "proto/validation_result.pb.h"
#include "riegeli/bytes/reader.h"
#include "utils/dual_status_tracker.h"
#include "utils/status_tracker.h"
#include "validator/result.h"
#include "validator/validation_result_internal.h"
namespace credentio {
namespace {
class BmffHashBindingHasherTracker : public BindingHasherTracker {
public:
explicit BmffHashBindingHasherTracker(StatusTracker* tracker,
absl::string_view hard_binding_uri)
: tracker_(*ABSL_DIE_IF_NULL(tracker)),
hard_binding_uri_(hard_binding_uri) {}
void RecordMismatch() override {
tracker_.RecordFailure(FailureStatusCode::kAssertionBmffHashMismatch,
{.url = hard_binding_uri_});
}
void RecordMalformed() override {
tracker_.RecordFailure(FailureStatusCode::kAssertionBmffHashMalformed,
{.url = hard_binding_uri_});
}
void RecordAlgorithmUnsupported() override {
tracker_.RecordFailure(FailureStatusCode::kAlgorithmUnsupported,
{.url = hard_binding_uri_});
}
void RecordGeneralError(absl::Status error) override {
tracker_.RecordFailure(
FailureStatusCode::kGeneralError,
{.url = hard_binding_uri_, .explanation = error.message()});
}
private:
StatusTracker& tracker_;
absl::string_view hard_binding_uri_;
};
} // namespace
absl::StatusOr<std::unique_ptr<ValidationResultProto>>
BmffHashHardBindingValidator::Validate(
riegeli::Reader& contents, const AssetByteInfo& asset_byte_info,
std::unique_ptr<PartialValidationResultProto> partial_validation_result)
const {
const Assertion* absl_nullable assertion =
GetAssertion(partial_validation_result.get(),
partial_validation_result->hard_binding_uri());
if (assertion == nullptr) {
return absl::InvalidArgumentError(absl::StrCat(
"missing assertion: ", partial_validation_result->hard_binding_uri()));
}
if (!assertion->has_bmff_based_hash()) {
return absl::InvalidArgumentError(
absl::StrCat("assertion is not a bmff hash assertion: ",
partial_validation_result->hard_binding_uri()));
}
ABSL_ASSIGN_OR_RETURN(auto tracker,
DualStatusTracker::FromPartialValidationResult(
partial_validation_result.get()));
Validate(contents, assertion->bmff_based_hash(),
partial_validation_result->hard_binding_uri(), *tracker);
return MakeFullValidationResult(std::move(partial_validation_result));
}
void BmffHashHardBindingValidator::Validate(
riegeli::Reader& contents, const BmffBasedHashAssertion& assertion,
absl::string_view hard_binding_uri, StatusTracker& tracker) const {
if (!contents.Seek(0) || contents.pos() != 0) {
tracker.RecordFailure(FailureStatusCode::kGoogleInternalError,
{.url = hard_binding_uri});
return;
}
if (HasAdditionalExclusions(assertion)) {
tracker.RecordInformational(
InformationalStatusCode::kAssertionBmffHashAdditionalExclusionsPresent,
{.url = hard_binding_uri});
}
BmffHashBindingHasherTracker hasher_tracker(&tracker, hard_binding_uri);
BmffBindingHasher hasher(&assertion, &hasher_tracker);
auto digest = hasher.Digest(contents);
if (digest.value_or("") != assertion.hash()) {
tracker.RecordFailure(FailureStatusCode::kAssertionBmffHashMismatch,
{.url = hard_binding_uri});
return;
}
if (assertion.merkles_size() > 0) {
MerkleValidator merkle_validator(contents, assertion, hard_binding_uri);
if (!merkle_validator.Validate(tracker).ok()) {
// Failure Codes were already recorded by ValidateMerkleMaps.
return;
}
}
tracker.RecordSuccess(SuccessStatusCode::kAssertionBmffHashMatch,
{.url = hard_binding_uri});
}
} // namespace credentio