// Copyright (c) 2020-present, Facebook, Inc. All rights reserved. // This source code is licensed under both the GPLv2 (found in the // COPYING file in the root directory) and Apache 2.0 License // (found in the LICENSE.Apache file in the root directory). // // This file contains classes containing fields to protect individual entries. // The classes are named "ProtectionInfo", where indicates the // combination of fields that are covered. Each field has a single letter // abbreviation as follows. // // K = key // V = value // O = optype aka value type // S = seqno // C = CF ID // // Then, for example, a class that protects an entry consisting of key, value, // optype, and CF ID (i.e., a `WriteBatch` entry) would be named // `ProtectionInfoKVOC`. // // The `ProtectionInfo.*` classes are templated on the integer type used to hold // the XOR of hashes for each field. Only unsigned integer types are supported, // and the maximum supported integer width is 64 bits. When the integer type is // narrower than the hash values, we lop off the most significant bits to make // them fit. // // The `ProtectionInfo.*` classes are all intended to be non-persistent. We do // not currently make the byte order consistent for integer fields before // hashing them, so the resulting values are endianness-dependent. #pragma once #include #include "db/dbformat.h" #include "rocksdb/types.h" #include "util/hash.h" namespace ROCKSDB_NAMESPACE { template class ProtectionInfo; template class ProtectionInfoKVO; template class ProtectionInfoKVOC; template class ProtectionInfoKVOS; template class ProtectionInfoKV; // Aliases for 64-bit protection infos. using ProtectionInfo64 = ProtectionInfo; using ProtectionInfoKVO64 = ProtectionInfoKVO; using ProtectionInfoKVOC64 = ProtectionInfoKVOC; using ProtectionInfoKVOS64 = ProtectionInfoKVOS; template class ProtectionInfo { public: ProtectionInfo() = default; Status GetStatus() const; ProtectionInfoKVO ProtectKVO(const Slice& key, const Slice& value, ValueType op_type) const; ProtectionInfoKVO ProtectKVO(const SliceParts& key, const SliceParts& value, ValueType op_type) const; ProtectionInfoKV ProtectKV(const Slice& key, const Slice& value) const; private: friend class ProtectionInfoKVO; friend class ProtectionInfoKVOS; friend class ProtectionInfoKVOC; friend class ProtectionInfoKV; // Each field is hashed with an independent value so we can catch fields being // swapped. Per the `NPHash64()` docs, using consecutive seeds is a pitfall, // and we should instead vary our seeds by a large odd number. This value by // which we increment (0xD28AAD72F49BD50B) was taken from // `head -c8 /dev/urandom | hexdump`, run repeatedly until it yielded an odd // number. The values are computed manually since the Windows C++ compiler // complains about the overflow when adding constants. static const uint64_t kSeedK = 0; static const uint64_t kSeedV = 0xD28AAD72F49BD50B; static const uint64_t kSeedO = 0xA5155AE5E937AA16; static const uint64_t kSeedS = 0x77A00858DDD37F21; static const uint64_t kSeedC = 0x4A2AB5CBD26F542C; ProtectionInfo(T val) : val_(val) { static_assert(sizeof(ProtectionInfo) == sizeof(T), ""); } T GetVal() const { return val_; } void SetVal(T val) { val_ = val; } void Encode(uint8_t len, char* dst) const { assert(sizeof(val_) >= len); switch (len) { case 1: dst[0] = static_cast(val_); break; case 2: EncodeFixed16(dst, static_cast(val_)); break; case 4: EncodeFixed32(dst, static_cast(val_)); break; case 8: EncodeFixed64(dst, static_cast(val_)); break; default: assert(false); } } bool Verify(uint8_t len, const char* checksum_ptr) const { assert(sizeof(val_) >= len); switch (len) { case 1: return static_cast(checksum_ptr[0]) == static_cast(val_); case 2: return DecodeFixed16(checksum_ptr) == static_cast(val_); case 4: return DecodeFixed32(checksum_ptr) == static_cast(val_); case 8: return DecodeFixed64(checksum_ptr) == static_cast(val_); default: assert(false); return false; } } T val_ = 0; }; template class ProtectionInfoKVO { public: ProtectionInfoKVO() = default; ProtectionInfo StripKVO(const Slice& key, const Slice& value, ValueType op_type) const; ProtectionInfo StripKVO(const SliceParts& key, const SliceParts& value, ValueType op_type) const; ProtectionInfoKVOC ProtectC(ColumnFamilyId column_family_id) const; ProtectionInfoKVOS ProtectS(SequenceNumber sequence_number) const; void UpdateK(const Slice& old_key, const Slice& new_key); void UpdateK(const SliceParts& old_key, const SliceParts& new_key); void UpdateV(const Slice& old_value, const Slice& new_value); void UpdateV(const SliceParts& old_value, const SliceParts& new_value); void UpdateO(ValueType old_op_type, ValueType new_op_type); // Encode this protection info into `len` bytes and stores them in `dst`. void Encode(uint8_t len, char* dst) const { info_.Encode(len, dst); } // Verify this protection info against the protection info encoded by Encode() // at the first `len` bytes of `checksum_ptr`. // Returns true iff the verification is successful. bool Verify(uint8_t len, const char* checksum_ptr) const { return info_.Verify(len, checksum_ptr); } private: friend class ProtectionInfo; friend class ProtectionInfoKVOS; friend class ProtectionInfoKVOC; explicit ProtectionInfoKVO(T val) : info_(val) { static_assert(sizeof(ProtectionInfoKVO) == sizeof(T), ""); } T GetVal() const { return info_.GetVal(); } void SetVal(T val) { info_.SetVal(val); } ProtectionInfo info_; }; template class ProtectionInfoKVOC { public: ProtectionInfoKVOC() = default; ProtectionInfoKVO StripC(ColumnFamilyId column_family_id) const; void UpdateK(const Slice& old_key, const Slice& new_key) { kvo_.UpdateK(old_key, new_key); } void UpdateK(const SliceParts& old_key, const SliceParts& new_key) { kvo_.UpdateK(old_key, new_key); } void UpdateV(const Slice& old_value, const Slice& new_value) { kvo_.UpdateV(old_value, new_value); } void UpdateV(const SliceParts& old_value, const SliceParts& new_value) { kvo_.UpdateV(old_value, new_value); } void UpdateO(ValueType old_op_type, ValueType new_op_type) { kvo_.UpdateO(old_op_type, new_op_type); } void UpdateC(ColumnFamilyId old_column_family_id, ColumnFamilyId new_column_family_id); void Encode(uint8_t len, char* dst) const { kvo_.Encode(len, dst); } bool Verify(uint8_t len, const char* checksum_ptr) const { return kvo_.Verify(len, checksum_ptr); } private: friend class ProtectionInfoKVO; explicit ProtectionInfoKVOC(T val) : kvo_(val) { static_assert(sizeof(ProtectionInfoKVOC) == sizeof(T), ""); } T GetVal() const { return kvo_.GetVal(); } void SetVal(T val) { kvo_.SetVal(val); } ProtectionInfoKVO kvo_; }; template class ProtectionInfoKVOS { public: ProtectionInfoKVOS() = default; ProtectionInfoKVO StripS(SequenceNumber sequence_number) const; void UpdateK(const Slice& old_key, const Slice& new_key) { kvo_.UpdateK(old_key, new_key); } void UpdateK(const SliceParts& old_key, const SliceParts& new_key) { kvo_.UpdateK(old_key, new_key); } void UpdateV(const Slice& old_value, const Slice& new_value) { kvo_.UpdateV(old_value, new_value); } void UpdateV(const SliceParts& old_value, const SliceParts& new_value) { kvo_.UpdateV(old_value, new_value); } void UpdateO(ValueType old_op_type, ValueType new_op_type) { kvo_.UpdateO(old_op_type, new_op_type); } void UpdateS(SequenceNumber old_sequence_number, SequenceNumber new_sequence_number); void Encode(uint8_t len, char* dst) const { kvo_.Encode(len, dst); } bool Verify(uint8_t len, const char* checksum_ptr) const { return kvo_.Verify(len, checksum_ptr); } private: friend class ProtectionInfoKVO; explicit ProtectionInfoKVOS(T val) : kvo_(val) { static_assert(sizeof(ProtectionInfoKVOS) == sizeof(T), ""); } T GetVal() const { return kvo_.GetVal(); } void SetVal(T val) { kvo_.SetVal(val); } ProtectionInfoKVO kvo_; }; template class ProtectionInfoKV { public: ProtectionInfoKV() = default; void Encode(uint8_t len, char* dst) const { info_.Encode(len, dst); } bool Verify(uint8_t len, const char* checksum_ptr) const { return info_.Verify(len, checksum_ptr); } private: friend class ProtectionInfo; explicit ProtectionInfoKV(T val) : info_(val) { static_assert(sizeof(ProtectionInfoKV) == sizeof(T)); } ProtectionInfo info_; }; template Status ProtectionInfo::GetStatus() const { if (val_ != 0) { return Status::Corruption("ProtectionInfo mismatch"); } return Status::OK(); } template ProtectionInfoKVO ProtectionInfo::ProtectKVO(const Slice& key, const Slice& value, ValueType op_type) const { T val = GetVal(); val = val ^ static_cast(GetSliceNPHash64(key, ProtectionInfo::kSeedK)); val = val ^ static_cast(GetSliceNPHash64(value, ProtectionInfo::kSeedV)); val = val ^ static_cast(NPHash64(reinterpret_cast(&op_type), sizeof(op_type), ProtectionInfo::kSeedO)); return ProtectionInfoKVO(val); } template ProtectionInfoKVO ProtectionInfo::ProtectKVO(const SliceParts& key, const SliceParts& value, ValueType op_type) const { T val = GetVal(); val = val ^ static_cast(GetSlicePartsNPHash64(key, ProtectionInfo::kSeedK)); val = val ^ static_cast(GetSlicePartsNPHash64(value, ProtectionInfo::kSeedV)); val = val ^ static_cast(NPHash64(reinterpret_cast(&op_type), sizeof(op_type), ProtectionInfo::kSeedO)); return ProtectionInfoKVO(val); } template ProtectionInfoKV ProtectionInfo::ProtectKV(const Slice& key, const Slice& value) const { T val = GetVal(); val = val ^ static_cast(GetSliceNPHash64(key, ProtectionInfo::kSeedK)); val = val ^ static_cast(GetSliceNPHash64(value, ProtectionInfo::kSeedV)); return ProtectionInfoKV(val); } template void ProtectionInfoKVO::UpdateK(const Slice& old_key, const Slice& new_key) { T val = GetVal(); val = val ^ static_cast(GetSliceNPHash64(old_key, ProtectionInfo::kSeedK)); val = val ^ static_cast(GetSliceNPHash64(new_key, ProtectionInfo::kSeedK)); SetVal(val); } template void ProtectionInfoKVO::UpdateK(const SliceParts& old_key, const SliceParts& new_key) { T val = GetVal(); val = val ^ static_cast( GetSlicePartsNPHash64(old_key, ProtectionInfo::kSeedK)); val = val ^ static_cast( GetSlicePartsNPHash64(new_key, ProtectionInfo::kSeedK)); SetVal(val); } template void ProtectionInfoKVO::UpdateV(const Slice& old_value, const Slice& new_value) { T val = GetVal(); val = val ^ static_cast(GetSliceNPHash64(old_value, ProtectionInfo::kSeedV)); val = val ^ static_cast(GetSliceNPHash64(new_value, ProtectionInfo::kSeedV)); SetVal(val); } template void ProtectionInfoKVO::UpdateV(const SliceParts& old_value, const SliceParts& new_value) { T val = GetVal(); val = val ^ static_cast( GetSlicePartsNPHash64(old_value, ProtectionInfo::kSeedV)); val = val ^ static_cast( GetSlicePartsNPHash64(new_value, ProtectionInfo::kSeedV)); SetVal(val); } template void ProtectionInfoKVO::UpdateO(ValueType old_op_type, ValueType new_op_type) { T val = GetVal(); val = val ^ static_cast(NPHash64(reinterpret_cast(&old_op_type), sizeof(old_op_type), ProtectionInfo::kSeedO)); val = val ^ static_cast(NPHash64(reinterpret_cast(&new_op_type), sizeof(new_op_type), ProtectionInfo::kSeedO)); SetVal(val); } template ProtectionInfo ProtectionInfoKVO::StripKVO(const Slice& key, const Slice& value, ValueType op_type) const { T val = GetVal(); val = val ^ static_cast(GetSliceNPHash64(key, ProtectionInfo::kSeedK)); val = val ^ static_cast(GetSliceNPHash64(value, ProtectionInfo::kSeedV)); val = val ^ static_cast(NPHash64(reinterpret_cast(&op_type), sizeof(op_type), ProtectionInfo::kSeedO)); return ProtectionInfo(val); } template ProtectionInfo ProtectionInfoKVO::StripKVO(const SliceParts& key, const SliceParts& value, ValueType op_type) const { T val = GetVal(); val = val ^ static_cast(GetSlicePartsNPHash64(key, ProtectionInfo::kSeedK)); val = val ^ static_cast(GetSlicePartsNPHash64(value, ProtectionInfo::kSeedV)); val = val ^ static_cast(NPHash64(reinterpret_cast(&op_type), sizeof(op_type), ProtectionInfo::kSeedO)); return ProtectionInfo(val); } template ProtectionInfoKVOC ProtectionInfoKVO::ProtectC( ColumnFamilyId column_family_id) const { T val = GetVal(); val = val ^ static_cast(NPHash64( reinterpret_cast(&column_family_id), sizeof(column_family_id), ProtectionInfo::kSeedC)); return ProtectionInfoKVOC(val); } template ProtectionInfoKVO ProtectionInfoKVOC::StripC( ColumnFamilyId column_family_id) const { T val = GetVal(); val = val ^ static_cast(NPHash64( reinterpret_cast(&column_family_id), sizeof(column_family_id), ProtectionInfo::kSeedC)); return ProtectionInfoKVO(val); } template void ProtectionInfoKVOC::UpdateC(ColumnFamilyId old_column_family_id, ColumnFamilyId new_column_family_id) { T val = GetVal(); val = val ^ static_cast(NPHash64( reinterpret_cast(&old_column_family_id), sizeof(old_column_family_id), ProtectionInfo::kSeedC)); val = val ^ static_cast(NPHash64( reinterpret_cast(&new_column_family_id), sizeof(new_column_family_id), ProtectionInfo::kSeedC)); SetVal(val); } template ProtectionInfoKVOS ProtectionInfoKVO::ProtectS( SequenceNumber sequence_number) const { T val = GetVal(); val = val ^ static_cast(NPHash64(reinterpret_cast(&sequence_number), sizeof(sequence_number), ProtectionInfo::kSeedS)); return ProtectionInfoKVOS(val); } template ProtectionInfoKVO ProtectionInfoKVOS::StripS( SequenceNumber sequence_number) const { T val = GetVal(); val = val ^ static_cast(NPHash64(reinterpret_cast(&sequence_number), sizeof(sequence_number), ProtectionInfo::kSeedS)); return ProtectionInfoKVO(val); } template void ProtectionInfoKVOS::UpdateS(SequenceNumber old_sequence_number, SequenceNumber new_sequence_number) { T val = GetVal(); val = val ^ static_cast(NPHash64( reinterpret_cast(&old_sequence_number), sizeof(old_sequence_number), ProtectionInfo::kSeedS)); val = val ^ static_cast(NPHash64( reinterpret_cast(&new_sequence_number), sizeof(new_sequence_number), ProtectionInfo::kSeedS)); SetVal(val); } } // namespace ROCKSDB_NAMESPACE