blob: 8c061f071ca0abcbd96bafb14e2142323abf20d7 [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.
//
#ifndef THIRD_PARTY_CREDENTIO_FORMATS_ZIP_READER_H_
#define THIRD_PARTY_CREDENTIO_FORMATS_ZIP_READER_H_
#include <cstdint>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "absl/base/attributes.h"
#include "absl/base/nullability.h"
#include "absl/status/statusor.h"
#include "absl/types/span.h"
#include "formats/byte_range.h"
#include "riegeli/bytes/reader.h"
namespace credentio {
// Zip Reader for C2PA, holds onto a reference to a `riegeli::Reader` which
// must outlive the reader.
class ZipReader {
public:
// Represents a file entry in a ZIP archive, derived from the file header in
// the central directory.
struct FileEntry {
uint64_t central_directory_header_offset = 0;
uint64_t local_file_header_offset = 0;
std::string file_name;
ByteRange file_range;
bool has_data_descriptor = false;
uint16_t general_purpose_bit_flag = 0;
uint16_t compression_method = 0;
uint32_t uncompressed_size = 0;
uint16_t extra_field_length = 0;
uint16_t file_comment_length = 0;
bool operator==(const FileEntry& other) const {
return central_directory_header_offset ==
other.central_directory_header_offset &&
local_file_header_offset == other.local_file_header_offset &&
file_name == other.file_name && file_range == other.file_range &&
has_data_descriptor == other.has_data_descriptor &&
general_purpose_bit_flag == other.general_purpose_bit_flag &&
compression_method == other.compression_method &&
uncompressed_size == other.uncompressed_size &&
extra_field_length == other.extra_field_length &&
file_comment_length == other.file_comment_length;
}
};
static absl::StatusOr<std::unique_ptr<ZipReader>> Create(
riegeli::Reader* absl_nonnull input ABSL_ATTRIBUTE_LIFETIME_BOUND);
// Sorts ZIP file entries by header offset and checks that they do not have
// duplicate offsets or overlapping ranges to prevent ZIP bomb conditions.
static absl::StatusOr<std::vector<ZipReader::FileEntry>> SortFileEntries(
absl::Span<const ZipReader::FileEntry> file_entries);
// Returns the offset of the start of the central directory.
uint64_t central_directory_offset() const {
return central_directory_offset_;
}
// Returns true if there is a file entry to read.
bool HasNext() const { return current_it_ < file_entries_.end(); };
// Reads the next file entry in the ZIP archive. Returns an OUT_OF_RANGE
// error if there are no more entries to read.
absl::StatusOr<FileEntry> Next();
private:
explicit ZipReader(std::vector<ZipReader::FileEntry>&& file_entries,
uint64_t central_directory_offset)
: file_entries_(std::move(file_entries)),
current_it_(file_entries_.begin()),
central_directory_offset_(central_directory_offset) {}
// All the file entries in the ZIP archive.
const std::vector<ZipReader::FileEntry> file_entries_;
std::vector<ZipReader::FileEntry>::const_iterator current_it_;
uint64_t central_directory_offset_;
};
} // namespace credentio
#endif // THIRD_PARTY_CREDENTIO_FORMATS_ZIP_READER_H_