blob: 18f2d92ea2286c7200bd054e1e47ffaf8b0fa605 [file]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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
*
* http://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 "iceberg/encryption/standard_key_metadata.h"
#include <utility>
#include "iceberg/util/macros.h"
namespace iceberg {
namespace {
void WriteLong(int64_t value, std::vector<uint8_t>& out) {
// Avro zig-zag varint encoding
auto n = (static_cast<uint64_t>(value) << 1) ^ static_cast<uint64_t>(value >> 63);
while (n & ~0x7FULL) {
out.push_back(static_cast<uint8_t>((n & 0x7F) | 0x80));
n >>= 7;
}
out.push_back(static_cast<uint8_t>(n));
}
void WriteBytes(std::span<const uint8_t> bytes, std::vector<uint8_t>& out) {
WriteLong(static_cast<int64_t>(bytes.size()), out);
out.insert(out.end(), bytes.begin(), bytes.end());
}
class Decoder {
public:
explicit Decoder(std::span<const uint8_t> buffer) : buffer_(buffer) {}
Result<int64_t> ReadLong() {
uint64_t n = 0;
int shift = 0;
while (true) {
if (pos_ >= buffer_.size()) {
return Invalid("Truncated key metadata");
}
if (shift > 63) {
return Invalid("Invalid varint in key metadata");
}
uint8_t b = buffer_[pos_++];
n |= static_cast<uint64_t>(b & 0x7F) << shift;
if ((b & 0x80) == 0) {
break;
}
shift += 7;
}
return static_cast<int64_t>((n >> 1) ^ -(n & 1));
}
/// \brief Read a zig-zag varint, limited to the five bytes of an Avro int.
Result<int32_t> ReadInt() {
uint32_t n = 0;
int shift = 0;
while (true) {
if (pos_ >= buffer_.size()) {
return Invalid("Truncated key metadata");
}
if (shift > 28) {
return Invalid("Invalid int varint in key metadata");
}
uint8_t b = buffer_[pos_++];
n |= static_cast<uint32_t>(b & 0x7F) << shift;
if ((b & 0x80) == 0) {
break;
}
shift += 7;
}
return static_cast<int32_t>((n >> 1) ^ -(n & 1));
}
Result<std::vector<uint8_t>> ReadBytes() {
ICEBERG_ASSIGN_OR_RAISE(auto length, ReadLong());
if (length < 0 || std::cmp_greater(length, buffer_.size() - pos_)) {
return Invalid("Invalid bytes length in key metadata: {}", length);
}
std::vector<uint8_t> bytes(buffer_.begin() + pos_, buffer_.begin() + pos_ + length);
pos_ += length;
return bytes;
}
/// \brief Read the branch index of a ["null", T] union; true if T is present.
///
/// Avro encodes the branch index as an int, so this rejects the longer
/// encodings that ReadLong accepts, as Java's readIndex does.
Result<bool> ReadOptionalBranch() {
ICEBERG_ASSIGN_OR_RAISE(auto branch, ReadInt());
if (branch != 0 && branch != 1) {
return Invalid("Invalid union branch in key metadata: {}", branch);
}
return branch == 1;
}
private:
std::span<const uint8_t> buffer_;
size_t pos_ = 0;
};
} // namespace
Result<StandardKeyMetadata> StandardKeyMetadata::Parse(std::span<const uint8_t> buffer) {
if (buffer.empty()) {
return Invalid("Empty key metadata");
}
if (buffer[0] != kV1) {
return NotSupported("Unsupported key metadata schema version: {}",
static_cast<int>(buffer[0]));
}
Decoder decoder(buffer.subspan(1));
StandardKeyMetadata metadata;
ICEBERG_ASSIGN_OR_RAISE(metadata.encryption_key, decoder.ReadBytes());
ICEBERG_ASSIGN_OR_RAISE(auto has_aad_prefix, decoder.ReadOptionalBranch());
if (has_aad_prefix) {
ICEBERG_ASSIGN_OR_RAISE(metadata.aad_prefix, decoder.ReadBytes());
}
ICEBERG_ASSIGN_OR_RAISE(auto has_file_length, decoder.ReadOptionalBranch());
if (has_file_length) {
ICEBERG_ASSIGN_OR_RAISE(metadata.file_length, decoder.ReadLong());
}
// Trailing bytes are ignored, matching Java, which decodes a single record.
return metadata;
}
std::vector<uint8_t> StandardKeyMetadata::Serialize() const {
std::vector<uint8_t> out;
out.push_back(kV1);
WriteBytes(encryption_key, out);
if (aad_prefix.has_value()) {
WriteLong(1, out);
WriteBytes(*aad_prefix, out);
} else {
WriteLong(0, out);
}
if (file_length.has_value()) {
WriteLong(1, out);
WriteLong(*file_length, out);
} else {
WriteLong(0, out);
}
return out;
}
StandardKeyMetadata StandardKeyMetadata::WithFileLength(int64_t length) const {
StandardKeyMetadata copy = *this;
copy.file_length = length;
return copy;
}
} // namespace iceberg