/** * Copyright (C) 2017 MongoDB Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * 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 * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * * 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 GNU Affero General 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 #include #include #include #include #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 class SHABlock { public: using HashType = typename Traits::HashType; static constexpr size_t kHashLength = sizeof(HashType); SHABlock() = default; SHABlock(HashType rawHash) : _hash(rawHash) {} /** * Constructs a SHABlock from a buffer of specified size. */ static StatusWith 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 SHABlock(newHash); } /** * Computes a hash of 'input' from multiple contigous buffers. */ static SHABlock computeHash(std::initializer_list input) { return SHABlock{Traits::computeHash(input)}; } /** * Computes a hash of 'input' from one buffer. */ static SHABlock computeHash(const uint8_t* input, size_t inputLen) { return computeHash({ConstDataRange(reinterpret_cast(input), inputLen)}); } /** * Computes a HMAC keyed hash of 'input' using the key 'key'. */ static SHABlock computeHmac(const uint8_t* key, size_t keyLen, const uint8_t* input, size_t inputLen) { SHABlock output; SHABlock::computeHmac(key, keyLen, input, inputLen, &output); return output; } /** * Computes a HMAC keyed hash of 'input' using the key 'key'. Writes the results into * a pre-allocated SHABlock. This lets us allocate SHABlocks with the SecureAllocator. */ static void computeHmac(const uint8_t* key, size_t keyLen, const uint8_t* input, size_t inputLen, SHABlock* const output) { return Traits::computeHmac(key, keyLen, input, inputLen, &(output->_hash)); } const uint8_t* data() const& { return _hash.data(); } uint8_t* data() && = delete; ConstDataRange toCDR() && = delete; ConstDataRange toCDR() const& { return ConstDataRange(reinterpret_cast(_hash.data()), kHashLength); } size_t size() const { return _hash.size(); } /** * Make a new SHABlock from a BSON BinData value. */ static StatusWith 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 SHABlock(newHash); } /** * Make a new SHABlock from a vector of bytes representing bindata. For IDL. */ static SHABlock fromBinData(const std::vector& 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 SHABlock(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 SHABlock and replace the current contents of this block * with the result. */ void xorInline(const SHABlock& 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(_hash.data()), _hash.size()); } bool operator==(const SHABlock& other) const { return consttimeMemEqual(this->_hash.data(), other._hash.data(), kHashLength); } bool operator!=(const SHABlock& other) const { return !(*this == other); } /** * Custom hasher so SHABlocks can be used in unordered data structures. * * ex: std::unordered_set shaSet; */ struct Hash { std::size_t operator()(const SHABlock& shaBlock) const { uint32_t hash; MurmurHash3_x86_32(shaBlock.data(), SHABlock::kHashLength, 0, &hash); return hash; } }; private: // The backing array of bytes for the sha block HashType _hash; }; template constexpr size_t SHABlock::kHashLength; template std::ostream& operator<<(std::ostream& os, const SHABlock& sha) { return os << sha.toString(); } } // namespace mongo