diff options
Diffstat (limited to 'src/mongo/crypto/hash_block.h')
-rw-r--r-- | src/mongo/crypto/hash_block.h | 237 |
1 files changed, 237 insertions, 0 deletions
diff --git a/src/mongo/crypto/hash_block.h b/src/mongo/crypto/hash_block.h new file mode 100644 index 00000000000..ac5a3017b9c --- /dev/null +++ b/src/mongo/crypto/hash_block.h @@ -0,0 +1,237 @@ +/** + * Copyright (C) 2018-present MongoDB, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the Server Side Public License, version 1, + * as published by MongoDB, Inc. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * Server Side Public License for more details. + * + * You should have received a copy of the Server Side Public License + * along with this program. If not, see + * <http://www.mongodb.com/licensing/server-side-public-license>. + * + * As a special exception, the copyright holders give permission to link the + * code of portions of this program with the OpenSSL library under certain + * conditions as described in each individual source file and distribute + * linked combinations including the program with the OpenSSL library. You + * must comply with the Server Side Public License in all respects for + * all of the code used other than as permitted herein. If you modify file(s) + * with this exception, you may extend this exception to your version of the + * file(s), but you are not obligated to do so. If you do not wish to do so, + * delete this exception statement from your version. If you delete this + * exception statement from all source files in the program, then also delete + * it in the license file. + */ + +#pragma once + +#include <array> +#include <cstddef> +#include <string> +#include <third_party/murmurhash3/MurmurHash3.h> +#include <vector> + +#include "mongo/base/data_range.h" +#include "mongo/base/status_with.h" +#include "mongo/bson/bsonmisc.h" +#include "mongo/bson/bsonobjbuilder.h" +#include "mongo/util/base64.h" +#include "mongo/util/secure_compare_memory.h" + +namespace mongo { + +struct BSONBinData; +class BSONObjBuilder; + +/** + * Data structure with fixed sized byte array that can be used as HMAC key or result of a SHA + * computation. + */ +template <typename Traits> +class HashBlock { +public: + using HashType = typename Traits::HashType; + static constexpr size_t kHashLength = sizeof(HashType); + static constexpr auto kName = Traits::name; + + HashBlock() = default; + + HashBlock(HashType rawHash) : _hash(rawHash) {} + + /** + * Constructs a HashBlock from a buffer of specified size. + */ + static StatusWith<HashBlock> fromBuffer(const uint8_t* input, size_t inputLen) { + if (inputLen != kHashLength) { + return { + ErrorCodes::InvalidLength, + str::stream() << "Unsupported " << Traits::name << " hash length: " << inputLen}; + } + + HashType newHash; + memcpy(newHash.data(), input, inputLen); + return HashBlock(newHash); + } + + /** + * Computes a hash of 'input' from multiple contigous buffers. + */ + static HashBlock computeHash(std::initializer_list<ConstDataRange> input) { + return HashBlock{Traits::computeHash(input)}; + } + + /** + * Computes a hash of 'input' from one buffer. + */ + static HashBlock computeHash(const uint8_t* input, size_t inputLen) { + return computeHash({ConstDataRange(input, inputLen)}); + } + + /** + * Computes a HMAC keyed hash of 'input' using the key 'key'. + */ + static HashBlock computeHmac(const uint8_t* key, + size_t keyLen, + const uint8_t* input, + size_t inputLen) { + HashBlock output; + HashBlock::computeHmac(key, keyLen, {ConstDataRange(input, inputLen)}, &output); + return output; + } + + /** + * Computes a HMAC keyed hash of 'input' using the key 'key'. Writes the results into + * a pre-allocated HashBlock. This lets us allocate HashBlocks with the SecureAllocator. + */ + static void computeHmac(const uint8_t* key, + size_t keyLen, + const uint8_t* input, + size_t inputLen, + HashBlock* const output) { + HashBlock::computeHmac(key, keyLen, {ConstDataRange(input, inputLen)}, output); + } + + static HashBlock computeHmac(const uint8_t* key, + size_t keyLen, + std::initializer_list<ConstDataRange> input) { + HashBlock output; + HashBlock::computeHmac(key, keyLen, input, &output); + return output; + } + + static void computeHmac(const uint8_t* key, + size_t keyLen, + std::initializer_list<ConstDataRange> input, + HashBlock* const output) { + Traits::computeHmac(key, keyLen, input, &(output->_hash)); + } + + const uint8_t* data() const& { + return _hash.data(); + } + + uint8_t* data() && = delete; + + ConstDataRange toCDR() && = delete; + + ConstDataRange toCDR() const& { + return ConstDataRange(reinterpret_cast<const char*>(_hash.data()), kHashLength); + } + + size_t size() const { + return _hash.size(); + } + + /** + * Make a new HashBlock from a BSON BinData value. + */ + static StatusWith<HashBlock> fromBinData(const BSONBinData& binData) { + if (binData.type != BinDataGeneral) { + return {ErrorCodes::UnsupportedFormat, + str::stream() << Traits::name << " only accepts BinDataGeneral type"}; + } + + if (binData.length != kHashLength) { + return {ErrorCodes::UnsupportedFormat, + str::stream() << "Unsupported " << Traits::name << " hash length: " + << binData.length}; + } + + HashType newHash; + memcpy(newHash.data(), binData.data, binData.length); + return HashBlock(newHash); + } + + /** + * Make a new HashBlock from a vector of bytes representing bindata. For IDL. + */ + static HashBlock fromBinData(const std::vector<unsigned char>& bytes) { + HashType newHash; + uassert(ErrorCodes::UnsupportedFormat, + str::stream() << "Unsupported " << Traits::name << " hash length: " << bytes.size(), + bytes.size() == kHashLength); + memcpy(newHash.data(), bytes.data(), bytes.size()); + return HashBlock(newHash); + } + + /** + * Append this to a builder using the given name as a BSON BinData type value. + */ + void appendAsBinData(BSONObjBuilder& builder, StringData fieldName) const { + builder.appendBinData(fieldName, _hash.size(), BinDataGeneral, _hash.data()); + } + + /** + * Do a bitwise xor against another HashBlock and replace the current contents of this block + * with the result. + */ + void xorInline(const HashBlock& other) { + for (size_t x = 0; x < _hash.size(); x++) { + _hash[x] ^= other._hash[x]; + } + } + + /** + * Base64 encoding of the sha block as a string. + */ + std::string toString() const { + return base64::encode(reinterpret_cast<const char*>(_hash.data()), _hash.size()); + } + + bool operator==(const HashBlock& other) const { + return consttimeMemEqual(this->_hash.data(), other._hash.data(), kHashLength); + } + + bool operator!=(const HashBlock& other) const { + return !(*this == other); + } + + /** + * Custom hasher so HashBlocks can be used in unordered data structures. + * + * ex: std::unordered_set<HashBlock, HashBlock::Hash> shaSet; + */ + struct Hash { + std::size_t operator()(const HashBlock& HashBlock) const { + uint32_t hash; + MurmurHash3_x86_32(HashBlock.data(), HashBlock::kHashLength, 0, &hash); + return hash; + } + }; + +private: + // The backing array of bytes for the sha block + HashType _hash; +}; + + +template <typename Traits> +std::ostream& operator<<(std::ostream& os, const HashBlock<Traits>& sha) { + return os << sha.toString(); +} + +} // namespace mongo |