blob: 9e1eb53b3100811fc1df5448baad9c218b82df7a [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 "formats/zip/hasher_data.h"
#include <cstdint>
#include <memory>
#include <string>
#include <vector>
#include "absl/base/nullability.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/types/span.h"
#include "bindings/input_hasher.h"
#include "crypto/algorithms.h"
#include "formats/zip/constants.h"
#include "formats/zip/reader.h"
#include "proto/collection_data_hash_assertion.pb.h"
#include "riegeli/bytes/reader.h"
namespace credentio {
absl::StatusOr<CollectionDataHashAssertion> CollectionDataHashTemplate(
absl::Span<const ZipReader::FileEntry> file_entries, HashAlgorithm alg) {
CollectionDataHashAssertion assertion;
assertion.set_alg(absl::StrCat(alg));
for (const ZipReader::FileEntry& file_entry : file_entries) {
if (file_entry.file_name == kZipManifestFileName) {
continue;
}
UriHashedData* uri_hashed_data = assertion.add_uris();
uri_hashed_data->set_uri(file_entry.file_name);
uri_hashed_data->set_hash(std::string(HashSizeBytes(alg), 0));
}
assertion.set_zip_central_directory_hash(std::string(HashSizeBytes(alg), 0));
return assertion;
}
absl::StatusOr<CollectionDataHashAssertion> DigestCollectionDataHash(
riegeli::Reader* absl_nonnull source, HashAlgorithm alg,
absl::Span<const ZipReader::FileEntry> file_entries,
uint64_t central_directory_offset) {
ABSL_ASSIGN_OR_RETURN(std::vector<ZipReader::FileEntry> sorted_entries,
ZipReader::SortFileEntries(file_entries));
if (!source->Size().has_value()) {
return absl::InternalError("source->Size() returned no value");
}
CollectionDataHashAssertion assertion;
assertion.set_alg(absl::StrCat(alg));
const ZipReader::FileEntry* manifest_file_entry = nullptr;
for (const ZipReader::FileEntry& file_entry : sorted_entries) {
if (file_entry.file_name == kZipManifestFileName) {
manifest_file_entry = &file_entry;
continue;
}
UriHashedData* uri_hashed_data = assertion.add_uris();
uri_hashed_data->set_uri(file_entry.file_name);
int64_t entry_end =
file_entry.file_range.offset + file_entry.file_range.length;
if (file_entry.has_data_descriptor) {
if (!source->Seek(entry_end) || source->pos() != entry_end) {
return absl::InternalError("Failed to seek to entry end.");
}
std::string buffer;
if (!source->Read(kZipDataDescriptorSignature.size(), buffer)) {
return absl::InternalError("Failed to read data descriptor signature.");
}
if (buffer == kZipDataDescriptorSignature) {
entry_end += kDataDescriptorWithSignatureSize;
} else {
entry_end += kDataDescriptorSize;
}
}
ABSL_ASSIGN_OR_RETURN(std::unique_ptr<InputHasher> hasher,
InputHasher::Create(alg));
ABSL_RETURN_IF_ERROR(
hasher->Update(*source, file_entry.local_file_header_offset,
entry_end - file_entry.local_file_header_offset));
uri_hashed_data->set_hash(hasher->Digest());
}
ABSL_ASSIGN_OR_RETURN(std::unique_ptr<InputHasher> hasher,
InputHasher::Create(alg));
if (!source->Seek(central_directory_offset) ||
source->pos() != central_directory_offset) {
return absl::InternalError("Failed to seek to central directory offset.");
}
if (manifest_file_entry == nullptr) {
return absl::InternalError("No manifest file entry found.");
}
uint64_t crc32_offset =
manifest_file_entry->central_directory_header_offset + 16;
ABSL_RETURN_IF_ERROR(hasher->Update(*source, central_directory_offset,
crc32_offset - central_directory_offset));
uint64_t after_crc32_offset = crc32_offset + 4;
ABSL_RETURN_IF_ERROR(
hasher->Update(*source, after_crc32_offset,
source->Size().value() - after_crc32_offset));
assertion.set_zip_central_directory_hash(hasher->Digest());
return assertion;
}
} // namespace credentio