// 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; // 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; T GetVal() const { return val_; } private: friend class ProtectionInfoKVO; friend class ProtectionInfoKVOS; friend class ProtectionInfoKVOC; // 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), ""); } void SetVal(T val) { val_ = val; } 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); T GetVal() const { return info_.GetVal(); } private: friend class ProtectionInfo; friend class ProtectionInfoKVOS; friend class ProtectionInfoKVOC; explicit ProtectionInfoKVO(T val) : info_(val) { static_assert(sizeof(ProtectionInfoKVO) == sizeof(T), ""); } 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); T GetVal() const { return kvo_.GetVal(); } private: friend class ProtectionInfoKVO; explicit ProtectionInfoKVOC(T val) : kvo_(val) { static_assert(sizeof(ProtectionInfoKVOC) == sizeof(T), ""); } 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); T GetVal() const { return kvo_.GetVal(); } private: friend class ProtectionInfoKVO; explicit ProtectionInfoKVOS(T val) : kvo_(val) { static_assert(sizeof(ProtectionInfoKVOS) == sizeof(T), ""); } void SetVal(T val) { kvo_.SetVal(val); } ProtectionInfoKVO kvo_; }; 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 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