diff options
author | Randolph Tan <randolph@10gen.com> | 2017-02-16 13:39:22 -0500 |
---|---|---|
committer | Randolph Tan <randolph@10gen.com> | 2017-02-24 11:56:25 -0500 |
commit | 71ce59a2648185c8e1ccd5b3a1af6ff05dbac7d7 (patch) | |
tree | 7567a790f1f6ae0388e607dc6c91bd8256036460 /src/mongo/crypto | |
parent | 973b8b9da39db84073e98d4979ec3a8d6179b217 (diff) | |
download | mongo-71ce59a2648185c8e1ccd5b3a1af6ff05dbac7d7.tar.gz |
SERVER-28052 Make SHA1Hash a full fledged class
Diffstat (limited to 'src/mongo/crypto')
-rw-r--r-- | src/mongo/crypto/SConscript | 26 | ||||
-rw-r--r-- | src/mongo/crypto/crypto.h | 53 | ||||
-rw-r--r-- | src/mongo/crypto/mechanism_scram.cpp | 120 | ||||
-rw-r--r-- | src/mongo/crypto/mechanism_scram.h | 14 | ||||
-rw-r--r-- | src/mongo/crypto/sha1_block.cpp | 88 | ||||
-rw-r--r-- | src/mongo/crypto/sha1_block.h | 99 | ||||
-rw-r--r-- | src/mongo/crypto/sha1_block_openssl.cpp (renamed from src/mongo/crypto/crypto_openssl.cpp) | 23 | ||||
-rw-r--r-- | src/mongo/crypto/sha1_block_test.cpp (renamed from src/mongo/crypto/crypto_test.cpp) | 107 | ||||
-rw-r--r-- | src/mongo/crypto/sha1_block_tom.cpp (renamed from src/mongo/crypto/crypto_tom.cpp) | 24 |
9 files changed, 369 insertions, 185 deletions
diff --git a/src/mongo/crypto/SConscript b/src/mongo/crypto/SConscript index c57bccedd31..a18405fbce5 100644 --- a/src/mongo/crypto/SConscript +++ b/src/mongo/crypto/SConscript @@ -13,22 +13,32 @@ env.SConscript( ], ) +env.Library('sha1_block', + source=[ + 'sha1_block.cpp' + ], + LIBDEPS=[ + '$BUILD_DIR/mongo/base', + ]) + env.Library( - target='crypto_tom', + target='sha1_block_tom', source=[ - 'crypto_tom.cpp' + 'sha1_block_tom.cpp' ], LIBDEPS=[ '$BUILD_DIR/mongo/base', + 'sha1_block', 'tom/tomcrypt' ]) -env.Library('crypto_openssl', +env.Library('sha1_block_openssl', source=[ - 'crypto_openssl.cpp' + 'sha1_block_openssl.cpp' ], LIBDEPS=[ '$BUILD_DIR/mongo/base', + 'sha1_block', ]) env.Library('scramauth', @@ -36,8 +46,8 @@ env.Library('scramauth', LIBDEPS=['$BUILD_DIR/mongo/base', '$BUILD_DIR/mongo/base/secure_allocator', '$BUILD_DIR/mongo/util/secure_zero_memory', - 'crypto_${MONGO_CRYPTO}']) + 'sha1_block_${MONGO_CRYPTO}']) -env.CppUnitTest('crypto_test', - ['crypto_test.cpp'], - LIBDEPS=['crypto_${MONGO_CRYPTO}']) +env.CppUnitTest('sha1_block_test', + ['sha1_block_test.cpp'], + LIBDEPS=['sha1_block_${MONGO_CRYPTO}']) diff --git a/src/mongo/crypto/crypto.h b/src/mongo/crypto/crypto.h deleted file mode 100644 index 1f010980078..00000000000 --- a/src/mongo/crypto/crypto.h +++ /dev/null @@ -1,53 +0,0 @@ -/** -* Copyright (C) 2014 10gen 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 <http://www.gnu.org/licenses/>. -* -* 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 <array> -#include <cstddef> - -namespace mongo { -// Storage container for a SHA1 hash -using SHA1Hash = std::array<std::uint8_t, 20>; - -namespace crypto { -/* - * Computes a SHA-1 hash of 'input'. - */ -SHA1Hash sha1(const unsigned char* input, const size_t inputLen); - -/* - * Computes a HMAC SHA-1 keyed hash of 'input' using the key 'key' - */ -SHA1Hash hmacSha1(const unsigned char* key, - const size_t keyLen, - const unsigned char* input, - const size_t inputLen); - -} // namespace crypto -} // namespace mongo diff --git a/src/mongo/crypto/mechanism_scram.cpp b/src/mongo/crypto/mechanism_scram.cpp index 57a2eed3dd0..a1b3c69d8fc 100644 --- a/src/mongo/crypto/mechanism_scram.cpp +++ b/src/mongo/crypto/mechanism_scram.cpp @@ -32,7 +32,6 @@ #include <vector> -#include "mongo/crypto/crypto.h" #include "mongo/platform/random.h" #include "mongo/util/base64.h" #include "mongo/util/secure_zero_memory.h" @@ -67,18 +66,14 @@ bool consttimeMemEqual(volatile const unsigned char* s1, // NOLINT - using vola } } // namespace -std::string hashToBase64(const SecureHandle<SHA1Hash>& hash) { - return base64::encode(reinterpret_cast<const char*>(hash->data()), hash->size()); -} - // Compute the SCRAM step Hi() as defined in RFC5802 -static SHA1Hash HMACIteration(const unsigned char input[], - size_t inputLen, - const unsigned char salt[], - size_t saltLen, - unsigned int iterationCount) { - SHA1Hash output; - SHA1Hash intermediateDigest; +static SHA1Block HMACIteration(const unsigned char input[], + size_t inputLen, + const unsigned char salt[], + size_t saltLen, + unsigned int iterationCount) { + SHA1Block output; + SHA1Block intermediateDigest; // Reserve a 20 byte block for the initial key. We use 16 byte salts, and must reserve an extra // 4 bytes for a suffix mandated by RFC5802. std::array<std::uint8_t, 20> startKey; @@ -92,25 +87,23 @@ static SHA1Hash HMACIteration(const unsigned char input[], startKey[saltLen + 3] = 1; // U1 = HMAC(input, salt + 0001) - output = crypto::hmacSha1(input, inputLen, startKey.data(), startKey.size()); + output = SHA1Block::computeHmac(input, inputLen, startKey.data(), startKey.size()); intermediateDigest = output; // intermediateDigest contains Ui and output contains the accumulated XOR:ed result for (size_t i = 2; i <= iterationCount; i++) { - intermediateDigest = - crypto::hmacSha1(input, inputLen, intermediateDigest.data(), intermediateDigest.size()); - for (size_t k = 0; k < output.size(); k++) { - output[k] ^= intermediateDigest[k]; - } + intermediateDigest = SHA1Block::computeHmac( + input, inputLen, intermediateDigest.data(), intermediateDigest.size()); + output.xorInline(intermediateDigest); } return output; } // Iterate the hash function to generate SaltedPassword -SHA1Hash generateSaltedPassword(const SCRAMPresecrets& presecrets) { +SHA1Block generateSaltedPassword(const SCRAMPresecrets& presecrets) { // saltedPassword = Hi(hashedPassword, salt) - SHA1Hash saltedPassword = + SHA1Block saltedPassword = HMACIteration(reinterpret_cast<const unsigned char*>(presecrets.hashedPassword.c_str()), presecrets.hashedPassword.size(), presecrets.salt.data(), @@ -121,30 +114,30 @@ SHA1Hash generateSaltedPassword(const SCRAMPresecrets& presecrets) { } SCRAMSecrets generateSecrets(const SCRAMPresecrets& presecrets) { - SHA1Hash saltedPassword = generateSaltedPassword(presecrets); + SHA1Block saltedPassword = generateSaltedPassword(presecrets); return generateSecrets(saltedPassword); } -SCRAMSecrets generateSecrets(const SHA1Hash& saltedPassword) { +SCRAMSecrets generateSecrets(const SHA1Block& saltedPassword) { SCRAMSecrets credentials; // ClientKey := HMAC(saltedPassword, "Client Key") credentials.clientKey = - crypto::hmacSha1(saltedPassword.data(), - saltedPassword.size(), - reinterpret_cast<const unsigned char*>(clientKeyConst.data()), - clientKeyConst.size()); + SHA1Block::computeHmac(saltedPassword.data(), + saltedPassword.size(), + reinterpret_cast<const unsigned char*>(clientKeyConst.data()), + clientKeyConst.size()); // StoredKey := H(clientKey) credentials.storedKey = - crypto::sha1(credentials.clientKey->data(), credentials.clientKey->size()); + SHA1Block::computeHash(credentials.clientKey->data(), credentials.clientKey->size()); // ServerKey := HMAC(SaltedPassword, "Server Key") credentials.serverKey = - crypto::hmacSha1(saltedPassword.data(), - saltedPassword.size(), - reinterpret_cast<const unsigned char*>(serverKeyConst.data()), - serverKeyConst.size()); + SHA1Block::computeHmac(saltedPassword.data(), + saltedPassword.size(), + reinterpret_cast<const unsigned char*>(serverKeyConst.data()), + serverKeyConst.size()); return credentials; } @@ -171,8 +164,8 @@ BSONObj generateCredentials(const std::string& hashedPassword, int iterationCoun saltLenQWords * sizeof(uint64_t)), iterationCount)); - std::string encodedStoredKey = hashToBase64(secrets.storedKey); - std::string encodedServerKey = hashToBase64(secrets.serverKey); + std::string encodedStoredKey = secrets.storedKey->toString(); + std::string encodedServerKey = secrets.serverKey->toString(); return BSON(iterationCountFieldName << iterationCount << saltFieldName << encodedUserSalt << storedKeyFieldName @@ -184,32 +177,27 @@ BSONObj generateCredentials(const std::string& hashedPassword, int iterationCoun std::string generateClientProof(const SCRAMSecrets& clientCredentials, const std::string& authMessage) { // ClientSignature := HMAC(StoredKey, AuthMessage) - SHA1Hash clientSignature = - crypto::hmacSha1(clientCredentials.storedKey->data(), - clientCredentials.storedKey->size(), - reinterpret_cast<const unsigned char*>(authMessage.c_str()), - authMessage.size()); - - // ClientProof := ClientKey XOR ClientSignature - SHA1Hash clientProof; - for (size_t i = 0; i < clientCredentials.clientKey->size(); i++) { - clientProof[i] = (*clientCredentials.clientKey)[i] ^ clientSignature[i]; - } - - return hashToBase64(clientProof); + SHA1Block clientSignature = + SHA1Block::computeHmac(clientCredentials.storedKey->data(), + clientCredentials.storedKey->size(), + reinterpret_cast<const unsigned char*>(authMessage.c_str()), + authMessage.size()); + + clientSignature.xorInline(*clientCredentials.clientKey); + return clientSignature.toString(); } bool verifyServerSignature(const SCRAMSecrets& clientCredentials, const std::string& authMessage, const std::string& receivedServerSignature) { // ServerSignature := HMAC(ServerKey, AuthMessage) - SHA1Hash serverSignature = - crypto::hmacSha1(clientCredentials.serverKey->data(), - clientCredentials.serverKey->size(), - reinterpret_cast<const unsigned char*>(authMessage.c_str()), - authMessage.size()); + SHA1Block serverSignature = + SHA1Block::computeHmac(clientCredentials.serverKey->data(), + clientCredentials.serverKey->size(), + reinterpret_cast<const unsigned char*>(authMessage.c_str()), + authMessage.size()); - std::string encodedServerSignature = hashToBase64(serverSignature); + std::string encodedServerSignature = serverSignature.toString(); if (encodedServerSignature.size() != receivedServerSignature.size()) { return false; @@ -223,20 +211,20 @@ bool verifyServerSignature(const SCRAMSecrets& clientCredentials, bool verifyClientProof(StringData clientProof, StringData storedKey, StringData authMessage) { // ClientSignature := HMAC(StoredKey, AuthMessage) - SHA1Hash clientSignature = - crypto::hmacSha1(reinterpret_cast<const unsigned char*>(storedKey.rawData()), - storedKey.size(), - reinterpret_cast<const unsigned char*>(authMessage.rawData()), - authMessage.size()); - - // ClientKey := ClientSignature XOR ClientProof - SHA1Hash clientKey; - for (size_t i = 0; i < clientKey.size(); i++) { - clientKey[i] = clientSignature[i] ^ clientProof.rawData()[i]; - } - - // StoredKey := H(ClientKey) - SHA1Hash computedStoredKey = crypto::sha1(clientKey.data(), clientKey.size()); + SHA1Block clientSignature = + SHA1Block::computeHmac(reinterpret_cast<const unsigned char*>(storedKey.rawData()), + storedKey.size(), + reinterpret_cast<const unsigned char*>(authMessage.rawData()), + authMessage.size()); + + auto clientProofSHA1Status = SHA1Block::fromBuffer( + reinterpret_cast<const uint8_t*>(clientProof.rawData()), clientProof.size()); + uassertStatusOK(clientProofSHA1Status); + clientSignature.xorInline(clientProofSHA1Status.getValue()); + + // StoredKey := H(clientSignature) + SHA1Block computedStoredKey = + SHA1Block::computeHash(clientSignature.data(), clientSignature.size()); return consttimeMemEqual(reinterpret_cast<const unsigned char*>(storedKey.rawData()), computedStoredKey.data(), diff --git a/src/mongo/crypto/mechanism_scram.h b/src/mongo/crypto/mechanism_scram.h index 1d3b86f2fdd..fb070e8162c 100644 --- a/src/mongo/crypto/mechanism_scram.h +++ b/src/mongo/crypto/mechanism_scram.h @@ -32,13 +32,11 @@ #include "mongo/base/secure_allocator.h" #include "mongo/base/status.h" -#include "mongo/crypto/crypto.h" +#include "mongo/crypto/sha1_block.h" #include "mongo/db/jsobj.h" namespace mongo { namespace scram { -// Convert a SHA1Hash into a base64 encoded string. -std::string hashToBase64(const SecureHandle<SHA1Hash>& hash); const std::string serverKeyConst = "Server Key"; const std::string clientKeyConst = "Client Key"; @@ -74,16 +72,16 @@ inline bool operator==(const SCRAMPresecrets& lhs, const SCRAMPresecrets& rhs) { /* * Computes the SaltedPassword from password, salt and iterationCount. */ -SHA1Hash generateSaltedPassword(const SCRAMPresecrets& presecrets); +SHA1Block generateSaltedPassword(const SCRAMPresecrets& presecrets); /* * Stores all of the keys, generated from a password, needed for a client or server to perform a * SCRAM handshake. This structure will secureZeroMemory itself on destruction. */ struct SCRAMSecrets { - SecureHandle<SHA1Hash> clientKey; - SecureHandle<SHA1Hash> storedKey; - SecureHandle<SHA1Hash> serverKey; + SecureHandle<SHA1Block> clientKey; + SecureHandle<SHA1Block> storedKey; + SecureHandle<SHA1Block> serverKey; }; /* @@ -95,7 +93,7 @@ SCRAMSecrets generateSecrets(const SCRAMPresecrets& presecrets); /* * Computes the ClientKey and StoredKey from SaltedPassword (client side). */ -SCRAMSecrets generateSecrets(const SHA1Hash& saltedPassword); +SCRAMSecrets generateSecrets(const SHA1Block& saltedPassword); /* * Generates the user salt and the SCRAM secrets storedKey and serverKey as diff --git a/src/mongo/crypto/sha1_block.cpp b/src/mongo/crypto/sha1_block.cpp new file mode 100644 index 00000000000..52880cd0438 --- /dev/null +++ b/src/mongo/crypto/sha1_block.cpp @@ -0,0 +1,88 @@ +/** + * 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 <http://www.gnu.org/licenses/>. + * + * 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. + */ + +#include "mongo/platform/basic.h" + +#include "mongo/crypto/sha1_block.h" + +#include "mongo/bson/bsonmisc.h" +#include "mongo/bson/bsonobjbuilder.h" +#include "mongo/util/base64.h" +#include "mongo/util/mongoutils/str.h" + +namespace mongo { + +constexpr size_t SHA1Block::kHashLength; + +SHA1Block::SHA1Block(HashType hash) : _hash(std::move(hash)) {} + +StatusWith<SHA1Block> SHA1Block::fromBuffer(const uint8_t* input, size_t inputLen) { + if (inputLen != kHashLength) { + return {ErrorCodes::InvalidLength, + str::stream() << "Unsupported SHA1Hash hash length: " << inputLen}; + } + + HashType newHash; + memcpy(newHash.data(), input, inputLen); + return SHA1Block(newHash); +} + +StatusWith<SHA1Block> SHA1Block::fromBinData(const BSONBinData& binData) { + if (binData.type != BinDataGeneral) { + return {ErrorCodes::UnsupportedFormat, "SHA1Block only accepts BinDataGeneral type"}; + } + + if (binData.length != kHashLength) { + return {ErrorCodes::UnsupportedFormat, + str::stream() << "Unsupported SHA1Block hash length: " << binData.length}; + } + + HashType newHash; + memcpy(newHash.data(), binData.data, binData.length); + return SHA1Block(newHash); +} + +std::string SHA1Block::toString() const { + return base64::encode(reinterpret_cast<const char*>(_hash.data()), _hash.size()); +} + +void SHA1Block::appendAsBinData(BSONObjBuilder& builder, StringData fieldName) { + builder.appendBinData(fieldName, _hash.size(), BinDataGeneral, _hash.data()); +} + +void SHA1Block::xorInline(const SHA1Block& other) { + for (size_t x = 0; x < _hash.size(); x++) { + _hash[x] ^= other._hash[x]; + } +} + +bool SHA1Block::operator==(const SHA1Block& rhs) const { + return rhs._hash == this->_hash; +} + +} // namespace mongo diff --git a/src/mongo/crypto/sha1_block.h b/src/mongo/crypto/sha1_block.h new file mode 100644 index 00000000000..588ceb8da52 --- /dev/null +++ b/src/mongo/crypto/sha1_block.h @@ -0,0 +1,99 @@ +/** +* 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 <http://www.gnu.org/licenses/>. +* +* 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 <array> +#include <cstddef> +#include <string> + +#include "mongo/base/status_with.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 SHA1 + * computation. + */ +class SHA1Block { +public: + static constexpr size_t kHashLength = 20; + using HashType = std::array<std::uint8_t, kHashLength>; + + SHA1Block() = default; + SHA1Block(HashType rawHash); + static StatusWith<SHA1Block> fromBuffer(const uint8_t* input, size_t inputLen); + + /** + * Computes a SHA-1 hash of 'input'. + */ + static SHA1Block computeHash(const uint8_t* input, size_t inputLen); + + /** + * Computes a HMAC SHA-1 keyed hash of 'input' using the key 'key' + */ + static SHA1Block computeHmac(const uint8_t* key, + size_t keyLen, + const uint8_t* input, + size_t inputLen); + + const uint8_t* data() const { + return _hash.data(); + } + + size_t size() const { + return _hash.size(); + } + + /** + * Make a new SHA1Block from a BSON BinData value. + */ + static StatusWith<SHA1Block> fromBinData(const BSONBinData& binData); + + /** + * Append this to a builder using the given name as a BSON BinData type value. + */ + void appendAsBinData(BSONObjBuilder& builder, StringData fieldName); + + /** + * Do a bitwise xor against another SHA1Block and replace the current contents of this block + * with the result. + */ + void xorInline(const SHA1Block& other); + + std::string toString() const; + bool operator==(const SHA1Block& rhs) const; + +private: + HashType _hash; +}; + +} // namespace mongo diff --git a/src/mongo/crypto/crypto_openssl.cpp b/src/mongo/crypto/sha1_block_openssl.cpp index bdcb23402cd..a3c4b8c4049 100644 --- a/src/mongo/crypto/crypto_openssl.cpp +++ b/src/mongo/crypto/sha1_block_openssl.cpp @@ -28,8 +28,9 @@ #include "mongo/platform/basic.h" +#include "mongo/crypto/sha1_block.h" + #include "mongo/config.h" -#include "mongo/crypto/crypto.h" #include "mongo/stdx/memory.h" #include "mongo/util/assert_util.h" @@ -62,12 +63,11 @@ void EVP_MD_CTX_free(EVP_MD_CTX* ctx) { #endif namespace mongo { -namespace crypto { /* * Computes a SHA-1 hash of 'input'. */ -SHA1Hash sha1(const unsigned char* input, const size_t inputLen) { - SHA1Hash output; +SHA1Block SHA1Block::computeHash(const uint8_t* input, size_t inputLen) { + HashType output; std::unique_ptr<EVP_MD_CTX, decltype(&EVP_MD_CTX_free)> digestCtx(EVP_MD_CTX_new(), EVP_MD_CTX_free); @@ -76,20 +76,19 @@ SHA1Hash sha1(const unsigned char* input, const size_t inputLen) { EVP_DigestInit_ex(digestCtx.get(), EVP_sha1(), NULL) == 1 && EVP_DigestUpdate(digestCtx.get(), input, inputLen) == 1 && EVP_DigestFinal_ex(digestCtx.get(), output.data(), NULL) == 1); - return output; + return SHA1Block(output); } /* * Computes a HMAC SHA-1 keyed hash of 'input' using the key 'key' */ -SHA1Hash hmacSha1(const unsigned char* key, - const size_t keyLen, - const unsigned char* input, - const size_t inputLen) { - SHA1Hash output; +SHA1Block SHA1Block::computeHmac(const uint8_t* key, + size_t keyLen, + const uint8_t* input, + size_t inputLen) { + HashType output; fassert(40380, HMAC(EVP_sha1(), key, keyLen, input, inputLen, output.data(), NULL) != NULL); - return output; + return SHA1Block(output); } -} // namespace crypto } // namespace mongo diff --git a/src/mongo/crypto/crypto_test.cpp b/src/mongo/crypto/sha1_block_test.cpp index 4857e36cab1..fa227f048a4 100644 --- a/src/mongo/crypto/crypto_test.cpp +++ b/src/mongo/crypto/sha1_block_test.cpp @@ -26,7 +26,11 @@ * then also delete it in the license file. */ -#include "mongo/crypto/crypto.h" +#include "mongo/platform/basic.h" + +#include "mongo/bson/bsonmisc.h" +#include "mongo/bson/bsonobjbuilder.h" +#include "mongo/crypto/sha1_block.h" #include "mongo/unittest/unittest.h" namespace mongo { @@ -35,21 +39,21 @@ namespace { // SHA-1 test vectors from http://csrc.nist.gov/groups/ST/toolkit/documents/Examples/SHA_All.pdf const struct { const char* msg; - SHA1Hash hash; -} sha1Tests[] = {{"abc", {0xa9, 0x99, 0x3e, 0x36, 0x47, 0x06, 0x81, 0x6a, 0xba, 0x3e, - 0x25, 0x71, 0x78, 0x50, 0xc2, 0x6c, 0x9c, 0xd0, 0xd8, 0x9d}}, + SHA1Block hash; +} sha1Tests[] = { + {"abc", SHA1Block::HashType{0xa9, 0x99, 0x3e, 0x36, 0x47, 0x06, 0x81, 0x6a, 0xba, 0x3e, + 0x25, 0x71, 0x78, 0x50, 0xc2, 0x6c, 0x9c, 0xd0, 0xd8, 0x9d}}, - {"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", - {0x84, 0x98, 0x3E, 0x44, 0x1C, 0x3B, 0xD2, 0x6E, 0xBA, 0xAE, - 0x4A, 0xA1, 0xF9, 0x51, 0x29, 0xE5, 0xE5, 0x46, 0x70, 0xF1}}}; + {"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", + SHA1Block::HashType{0x84, 0x98, 0x3E, 0x44, 0x1C, 0x3B, 0xD2, 0x6E, 0xBA, 0xAE, + 0x4A, 0xA1, 0xF9, 0x51, 0x29, 0xE5, 0xE5, 0x46, 0x70, 0xF1}}}; TEST(CryptoVectors, SHA1) { size_t numTests = sizeof(sha1Tests) / sizeof(sha1Tests[0]); for (size_t i = 0; i < numTests; i++) { - SHA1Hash result = crypto::sha1(reinterpret_cast<const unsigned char*>(sha1Tests[i].msg), - strlen(sha1Tests[i].msg)); - ASSERT(0 == memcmp(sha1Tests[i].hash.data(), result.data(), result.size())) - << "Failed SHA1 iteration " << i; + SHA1Block result = SHA1Block::computeHash( + reinterpret_cast<const unsigned char*>(sha1Tests[i].msg), strlen(sha1Tests[i].msg)); + ASSERT(sha1Tests[i].hash == result) << "Failed SHA1 iteration " << i; } } @@ -61,7 +65,7 @@ const struct { int keyLen; unsigned char data[maxDataSize]; int dataLen; - SHA1Hash hash; + SHA1Block hash; } hmacSha1Tests[] = { // RFC test case 1 {{0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, @@ -69,8 +73,8 @@ const struct { 20, {0x48, 0x69, 0x20, 0x54, 0x68, 0x65, 0x72, 0x65}, 8, - {0xb6, 0x17, 0x31, 0x86, 0x55, 0x05, 0x72, 0x64, 0xe2, 0x8b, - 0xc0, 0xb6, 0xfb, 0x37, 0x8c, 0x8e, 0xf1, 0x46, 0xbe, 0x00}}, + SHA1Block::HashType{0xb6, 0x17, 0x31, 0x86, 0x55, 0x05, 0x72, 0x64, 0xe2, 0x8b, + 0xc0, 0xb6, 0xfb, 0x37, 0x8c, 0x8e, 0xf1, 0x46, 0xbe, 0x00}}, // RFC test case 3 {{0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, @@ -81,8 +85,8 @@ const struct { 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd}, 50, - {0x12, 0x5d, 0x73, 0x42, 0xb9, 0xac, 0x11, 0xcd, 0x91, 0xa3, - 0x9a, 0xf4, 0x8a, 0xa1, 0x7b, 0x4f, 0x63, 0xf1, 0x75, 0xd3}}, + SHA1Block::HashType{0x12, 0x5d, 0x73, 0x42, 0xb9, 0xac, 0x11, 0xcd, 0x91, 0xa3, + 0x9a, 0xf4, 0x8a, 0xa1, 0x7b, 0x4f, 0x63, 0xf1, 0x75, 0xd3}}, // RFC test case 4 {{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, @@ -93,8 +97,8 @@ const struct { 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd}, 50, - {0x4c, 0x90, 0x07, 0xf4, 0x02, 0x62, 0x50, 0xc6, 0xbc, 0x84, - 0x14, 0xf9, 0xbf, 0x50, 0xc8, 0x6c, 0x2d, 0x72, 0x35, 0xda}}, + SHA1Block::HashType{0x4c, 0x90, 0x07, 0xf4, 0x02, 0x62, 0x50, 0xc6, 0xbc, 0x84, + 0x14, 0xf9, 0xbf, 0x50, 0xc8, 0x6c, 0x2d, 0x72, 0x35, 0xda}}, // RFC test case 6 {{0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, @@ -109,19 +113,70 @@ const struct { 0x2d, 0x53, 0x69, 0x7a, 0x65, 0x20, 0x4b, 0x65, 0x79, 0x20, 0x2d, 0x20, 0x48, 0x61, 0x73, 0x68, 0x20, 0x4b, 0x65, 0x79, 0x20, 0x46, 0x69, 0x72, 0x73, 0x74}, 54, - {0xaa, 0x4a, 0xe5, 0xe1, 0x52, 0x72, 0xd0, 0x0e, 0x95, 0x70, - 0x56, 0x37, 0xce, 0x8a, 0x3b, 0x55, 0xed, 0x40, 0x21, 0x12}}}; + SHA1Block::HashType{0xaa, 0x4a, 0xe5, 0xe1, 0x52, 0x72, 0xd0, 0x0e, 0x95, 0x70, + 0x56, 0x37, 0xce, 0x8a, 0x3b, 0x55, 0xed, 0x40, 0x21, 0x12}}}; TEST(CryptoVectors, HMACSHA1) { size_t numTests = sizeof(hmacSha1Tests) / sizeof(hmacSha1Tests[0]); for (size_t i = 0; i < numTests; i++) { - SHA1Hash result = crypto::hmacSha1(hmacSha1Tests[i].key, - hmacSha1Tests[i].keyLen, - hmacSha1Tests[i].data, - hmacSha1Tests[i].dataLen); - ASSERT(0 == memcmp(hmacSha1Tests[i].hash.data(), result.data(), result.size())) - << "Failed HMAC-SHA1 iteration " << i; + SHA1Block result = SHA1Block::computeHmac(hmacSha1Tests[i].key, + hmacSha1Tests[i].keyLen, + hmacSha1Tests[i].data, + hmacSha1Tests[i].dataLen); + ASSERT(hmacSha1Tests[i].hash == result) << "Failed HMAC-SHA1 iteration " << i; } } + +TEST(SHA1Block, BinDataRoundTrip) { + SHA1Block::HashType rawHash; + rawHash.fill(0); + for (size_t i = 0; i < rawHash.size(); i++) { + rawHash[i] = i; + } + + SHA1Block testHash(rawHash); + + BSONObjBuilder builder; + testHash.appendAsBinData(builder, "hash"); + auto newObj = builder.done(); + + auto hashElem = newObj["hash"]; + ASSERT_EQ(BinData, hashElem.type()); + ASSERT_EQ(BinDataGeneral, hashElem.binDataType()); + + int binLen = 0; + auto rawBinData = hashElem.binData(binLen); + ASSERT_EQ(SHA1Block::kHashLength, static_cast<size_t>(binLen)); + + auto newHashStatus = + SHA1Block::fromBinData(BSONBinData(rawBinData, binLen, hashElem.binDataType())); + ASSERT_OK(newHashStatus.getStatus()); + ASSERT_TRUE(testHash == newHashStatus.getValue()); +} + +TEST(SHA1Block, CanOnlyConstructFromBinGeneral) { + std::string dummy(SHA1Block::kHashLength, 'x'); + + auto newHashStatus = SHA1Block::fromBinData(BSONBinData(dummy.c_str(), dummy.size(), newUUID)); + ASSERT_EQ(ErrorCodes::UnsupportedFormat, newHashStatus.getStatus()); +} + +TEST(SHA1Block, FromBinDataShouldRegectWrongSize) { + std::string dummy(SHA1Block::kHashLength - 1, 'x'); + + auto newHashStatus = + SHA1Block::fromBinData(BSONBinData(dummy.c_str(), dummy.size(), BinDataGeneral)); + ASSERT_EQ(ErrorCodes::UnsupportedFormat, newHashStatus.getStatus()); +} + +TEST(SHA1Block, FromBufferShouldRejectWrongLength) { + std::string dummy(SHA1Block::kHashLength - 1, 'x'); + + auto newHashStatus = + SHA1Block::fromBuffer(reinterpret_cast<const uint8_t*>(dummy.c_str()), dummy.size()); + ASSERT_EQ(ErrorCodes::InvalidLength, newHashStatus.getStatus()); +} + + } // namespace } // namespace mongo diff --git a/src/mongo/crypto/crypto_tom.cpp b/src/mongo/crypto/sha1_block_tom.cpp index 904eb5925e3..d1078e60458 100644 --- a/src/mongo/crypto/crypto_tom.cpp +++ b/src/mongo/crypto/sha1_block_tom.cpp @@ -28,8 +28,9 @@ #include "mongo/platform/basic.h" +#include "mongo/crypto/sha1_block.h" + #include "mongo/config.h" -#include "mongo/crypto/crypto.h" #include "mongo/util/assert_util.h" #ifdef MONGO_CONFIG_SSL @@ -39,30 +40,30 @@ #include "mongo/crypto/tom/tomcrypt.h" namespace mongo { -namespace crypto { + /* * Computes a SHA-1 hash of 'input'. */ -SHA1Hash sha1(const unsigned char* input, const size_t inputLen) { - SHA1Hash output; +SHA1Block SHA1Block::computeHash(const uint8_t* input, size_t inputLen) { + HashType output; hash_state hashState; fassert(40381, sha1_init(&hashState) == CRYPT_OK && sha1_process(&hashState, input, inputLen) == CRYPT_OK && sha1_done(&hashState, output.data()) == CRYPT_OK); - return output; + return SHA1Block(output); } /* * Computes a HMAC SHA-1 keyed hash of 'input' using the key 'key' */ -SHA1Hash hmacSha1(const unsigned char* key, - const size_t keyLen, - const unsigned char* input, - const size_t inputLen) { +SHA1Block SHA1Block::computeHmac(const uint8_t* key, + size_t keyLen, + const uint8_t* input, + size_t inputLen) { invariant(key && input); - SHA1Hash output; + HashType output; static int hashId = -1; if (hashId == -1) { @@ -74,8 +75,7 @@ SHA1Hash hmacSha1(const unsigned char* key, fassert(40382, hmac_memory(hashId, key, keyLen, input, inputLen, output.data(), &sha1HashLen) == CRYPT_OK); - return output; + return SHA1Block(output); } -} // namespace crypto } // namespace mongo |