summaryrefslogtreecommitdiff
path: root/src/mongo/crypto
diff options
context:
space:
mode:
authorRandolph Tan <randolph@10gen.com>2017-02-16 13:39:22 -0500
committerRandolph Tan <randolph@10gen.com>2017-02-24 11:56:25 -0500
commit71ce59a2648185c8e1ccd5b3a1af6ff05dbac7d7 (patch)
tree7567a790f1f6ae0388e607dc6c91bd8256036460 /src/mongo/crypto
parent973b8b9da39db84073e98d4979ec3a8d6179b217 (diff)
downloadmongo-71ce59a2648185c8e1ccd5b3a1af6ff05dbac7d7.tar.gz
SERVER-28052 Make SHA1Hash a full fledged class
Diffstat (limited to 'src/mongo/crypto')
-rw-r--r--src/mongo/crypto/SConscript26
-rw-r--r--src/mongo/crypto/crypto.h53
-rw-r--r--src/mongo/crypto/mechanism_scram.cpp120
-rw-r--r--src/mongo/crypto/mechanism_scram.h14
-rw-r--r--src/mongo/crypto/sha1_block.cpp88
-rw-r--r--src/mongo/crypto/sha1_block.h99
-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