// 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 // T = timestamp // S = seqno // C = CF ID // // Then, for example, a class that protects an entry consisting of key, value, // optype, timestamp, and CF ID (i.e., a `WriteBatch` entry) would be named // `ProtectionInfoKVOTC`. // // 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 ProtectionInfoKVOT; template class ProtectionInfoKVOTC; template class ProtectionInfoKVOTS; // Aliases for 64-bit protection infos. typedef ProtectionInfo ProtectionInfo64; typedef ProtectionInfoKVOT ProtectionInfoKVOT64; typedef ProtectionInfoKVOTC ProtectionInfoKVOTC64; typedef ProtectionInfoKVOTS ProtectionInfoKVOTS64; template class ProtectionInfo { public: ProtectionInfo() = default; Status GetStatus() const; ProtectionInfoKVOT ProtectKVOT(const Slice& key, const Slice& value, ValueType op_type, const Slice& timestamp) const; ProtectionInfoKVOT ProtectKVOT(const SliceParts& key, const SliceParts& value, ValueType op_type, const Slice& timestamp) const; private: friend class ProtectionInfoKVOT; friend class ProtectionInfoKVOTS; friend class ProtectionInfoKVOTC; // 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 kSeedT = 0x77A00858DDD37F21; static const uint64_t kSeedS = 0x4A2AB5CBD26F542C; static const uint64_t kSeedC = 0x1CB5633EC70B2937; ProtectionInfo(T val) : val_(val) { static_assert(sizeof(ProtectionInfo) == sizeof(T), ""); } T GetVal() const { return val_; } void SetVal(T val) { val_ = val; } T val_ = 0; }; template class ProtectionInfoKVOT { public: ProtectionInfoKVOT() = default; ProtectionInfo StripKVOT(const Slice& key, const Slice& value, ValueType op_type, const Slice& timestamp) const; ProtectionInfo StripKVOT(const SliceParts& key, const SliceParts& value, ValueType op_type, const Slice& timestamp) const; ProtectionInfoKVOTC ProtectC(ColumnFamilyId column_family_id) const; ProtectionInfoKVOTS 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); void UpdateT(const Slice& old_timestamp, const Slice& new_timestamp); private: friend class ProtectionInfo; friend class ProtectionInfoKVOTS; friend class ProtectionInfoKVOTC; ProtectionInfoKVOT(T val) : info_(val) { static_assert(sizeof(ProtectionInfoKVOT) == sizeof(T), ""); } T GetVal() const { return info_.GetVal(); } void SetVal(T val) { info_.SetVal(val); } ProtectionInfo info_; }; template class ProtectionInfoKVOTC { public: ProtectionInfoKVOTC() = default; ProtectionInfoKVOT StripC(ColumnFamilyId column_family_id) const; void UpdateK(const Slice& old_key, const Slice& new_key) { kvot_.UpdateK(old_key, new_key); } void UpdateK(const SliceParts& old_key, const SliceParts& new_key) { kvot_.UpdateK(old_key, new_key); } void UpdateV(const Slice& old_value, const Slice& new_value) { kvot_.UpdateV(old_value, new_value); } void UpdateV(const SliceParts& old_value, const SliceParts& new_value) { kvot_.UpdateV(old_value, new_value); } void UpdateO(ValueType old_op_type, ValueType new_op_type) { kvot_.UpdateO(old_op_type, new_op_type); } void UpdateT(const Slice& old_timestamp, const Slice& new_timestamp) { kvot_.UpdateT(old_timestamp, new_timestamp); } void UpdateC(ColumnFamilyId old_column_family_id, ColumnFamilyId new_column_family_id); private: friend class ProtectionInfoKVOT; ProtectionInfoKVOTC(T val) : kvot_(val) { static_assert(sizeof(ProtectionInfoKVOTC) == sizeof(T), ""); } T GetVal() const { return kvot_.GetVal(); } void SetVal(T val) { kvot_.SetVal(val); } ProtectionInfoKVOT kvot_; }; template class ProtectionInfoKVOTS { public: ProtectionInfoKVOTS() = default; ProtectionInfoKVOT StripS(SequenceNumber sequence_number) const; void UpdateK(const Slice& old_key, const Slice& new_key) { kvot_.UpdateK(old_key, new_key); } void UpdateK(const SliceParts& old_key, const SliceParts& new_key) { kvot_.UpdateK(old_key, new_key); } void UpdateV(const Slice& old_value, const Slice& new_value) { kvot_.UpdateV(old_value, new_value); } void UpdateV(const SliceParts& old_value, const SliceParts& new_value) { kvot_.UpdateV(old_value, new_value); } void UpdateO(ValueType old_op_type, ValueType new_op_type) { kvot_.UpdateO(old_op_type, new_op_type); } void UpdateT(const Slice& old_timestamp, const Slice& new_timestamp) { kvot_.UpdateT(old_timestamp, new_timestamp); } void UpdateS(SequenceNumber old_sequence_number, SequenceNumber new_sequence_number); private: friend class ProtectionInfoKVOT; ProtectionInfoKVOTS(T val) : kvot_(val) { static_assert(sizeof(ProtectionInfoKVOTS) == sizeof(T), ""); } T GetVal() const { return kvot_.GetVal(); } void SetVal(T val) { kvot_.SetVal(val); } ProtectionInfoKVOT kvot_; }; template Status ProtectionInfo::GetStatus() const { if (val_ != 0) { return Status::Corruption("ProtectionInfo mismatch"); } return Status::OK(); } template ProtectionInfoKVOT ProtectionInfo::ProtectKVOT( const Slice& key, const Slice& value, ValueType op_type, const Slice& timestamp) 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)); val = val ^ static_cast(GetSliceNPHash64(timestamp, ProtectionInfo::kSeedT)); return ProtectionInfoKVOT(val); } template ProtectionInfoKVOT ProtectionInfo::ProtectKVOT( const SliceParts& key, const SliceParts& value, ValueType op_type, const Slice& timestamp) 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)); val = val ^ static_cast(GetSliceNPHash64(timestamp, ProtectionInfo::kSeedT)); return ProtectionInfoKVOT(val); } template void ProtectionInfoKVOT::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 ProtectionInfoKVOT::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 ProtectionInfoKVOT::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 ProtectionInfoKVOT::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 ProtectionInfoKVOT::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 void ProtectionInfoKVOT::UpdateT(const Slice& old_timestamp, const Slice& new_timestamp) { T val = GetVal(); val = val ^ static_cast( GetSliceNPHash64(old_timestamp, ProtectionInfo::kSeedT)); val = val ^ static_cast( GetSliceNPHash64(new_timestamp, ProtectionInfo::kSeedT)); SetVal(val); } template ProtectionInfo ProtectionInfoKVOT::StripKVOT( const Slice& key, const Slice& value, ValueType op_type, const Slice& timestamp) 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)); val = val ^ static_cast(GetSliceNPHash64(timestamp, ProtectionInfo::kSeedT)); return ProtectionInfo(val); } template ProtectionInfo ProtectionInfoKVOT::StripKVOT( const SliceParts& key, const SliceParts& value, ValueType op_type, const Slice& timestamp) 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)); val = val ^ static_cast(GetSliceNPHash64(timestamp, ProtectionInfo::kSeedT)); return ProtectionInfo(val); } template ProtectionInfoKVOTC ProtectionInfoKVOT::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 ProtectionInfoKVOTC(val); } template ProtectionInfoKVOT ProtectionInfoKVOTC::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 ProtectionInfoKVOT(val); } template void ProtectionInfoKVOTC::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 ProtectionInfoKVOTS ProtectionInfoKVOT::ProtectS( SequenceNumber sequence_number) const { T val = GetVal(); val = val ^ static_cast(NPHash64(reinterpret_cast(&sequence_number), sizeof(sequence_number), ProtectionInfo::kSeedS)); return ProtectionInfoKVOTS(val); } template ProtectionInfoKVOT ProtectionInfoKVOTS::StripS( SequenceNumber sequence_number) const { T val = GetVal(); val = val ^ static_cast(NPHash64(reinterpret_cast(&sequence_number), sizeof(sequence_number), ProtectionInfo::kSeedS)); return ProtectionInfoKVOT(val); } template void ProtectionInfoKVOTS::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