summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRandolph Tan <randolph@10gen.com>2017-02-16 13:39:22 -0500
committerSpencer Jackson <spencer.jackson@mongodb.com>2017-07-11 16:20:07 -0400
commitb686a69d6ed4653c6973dc62b50eb7b40df87fd4 (patch)
tree3779afe3bbcea4a8ab7e7d1ca902737b09748619
parent16e83332ed20e4054324a1a7714506e74eed5180 (diff)
downloadmongo-b686a69d6ed4653c6973dc62b50eb7b40df87fd4.tar.gz
SERVER-28052 Make SHA1Hash a full fledged class
(cherry picked from commit 71ce59a2648185c8e1ccd5b3a1af6ff05dbac7d7) (cherry picked from commit 2e686c169d07ed06530aa650ced310ee7b1adfc6)
-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)24
-rw-r--r--src/mongo/crypto/sha1_block_test.cpp (renamed from src/mongo/crypto/crypto_test.cpp)216
-rw-r--r--src/mongo/crypto/sha1_block_tom.cpp (renamed from src/mongo/crypto/crypto_tom.cpp)24
-rw-r--r--src/mongo/db/auth/sasl_scramsha1_server_conversation.cpp14
10 files changed, 377 insertions, 301 deletions
diff --git a/src/mongo/crypto/SConscript b/src/mongo/crypto/SConscript
index 9013c70b207..5b8ed85a684 100644
--- a/src/mongo/crypto/SConscript
+++ b/src/mongo/crypto/SConscript
@@ -8,22 +8,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',
@@ -31,8 +41,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 5d1cb784011..b4a543c6a54 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 << encodedStoredKey
@@ -182,32 +175,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;
@@ -221,20 +209,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..43565419a7d
--- /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 {
+
+const size_t SHA1Block::kHashLength = 20;
+
+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..280177de309
--- /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 const size_t kHashLength; // 20
+ using HashType = std::array<uint8_t, 20>;
+
+ 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 d7e310e0d46..50d18b9bace 100644
--- a/src/mongo/crypto/crypto_openssl.cpp
+++ b/src/mongo/crypto/sha1_block_openssl.cpp
@@ -28,8 +28,10 @@
#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"
#include "mongo/util/scopeguard.h"
@@ -62,12 +64,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;
EVP_MD_CTX digestCtx;
EVP_MD_CTX_init(&digestCtx);
@@ -77,20 +78,19 @@ SHA1Hash sha1(const unsigned char* input, const size_t inputLen) {
EVP_DigestInit_ex(&digestCtx, EVP_sha1(), NULL) == 1 &&
EVP_DigestUpdate(&digestCtx, input, inputLen) == 1 &&
EVP_DigestFinal_ex(&digestCtx, 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 8c8d3928c75..111132ff388 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,58 +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;
}
}
@@ -98,7 +65,7 @@ const struct {
int keyLen;
unsigned char data[maxDataSize];
int dataLen;
- SHA1Hash hash;
+ SHA1Block hash;
} hmacSha1Tests[] = {
// RFC test case 1
{{0x0b,
@@ -124,26 +91,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,
@@ -218,26 +167,8 @@ const struct {
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,
@@ -317,26 +248,8 @@ const struct {
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,
@@ -475,37 +388,70 @@ const struct {
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
diff --git a/src/mongo/db/auth/sasl_scramsha1_server_conversation.cpp b/src/mongo/db/auth/sasl_scramsha1_server_conversation.cpp
index 11848fb5ba9..5809306456d 100644
--- a/src/mongo/db/auth/sasl_scramsha1_server_conversation.cpp
+++ b/src/mongo/db/auth/sasl_scramsha1_server_conversation.cpp
@@ -35,8 +35,8 @@
#include <boost/algorithm/string/join.hpp>
#include <boost/algorithm/string/replace.hpp>
-#include "mongo/crypto/crypto.h"
#include "mongo/crypto/mechanism_scram.h"
+#include "mongo/crypto/sha1_block.h"
#include "mongo/db/auth/sasl_options.h"
#include "mongo/platform/random.h"
#include "mongo/util/base64.h"
@@ -284,14 +284,14 @@ StatusWith<bool> SaslSCRAMSHA1ServerConversation::_secondStep(const std::vector<
// ServerSignature := HMAC(ServerKey, AuthMessage)
std::string decodedServerKey = base64::decode(_creds.scram.serverKey);
- SHA1Hash serverSignature =
- crypto::hmacSha1(reinterpret_cast<const unsigned char*>(decodedServerKey.c_str()),
- decodedServerKey.size(),
- reinterpret_cast<const unsigned char*>(_authMessage.c_str()),
- _authMessage.size());
+ SHA1Block serverSignature =
+ SHA1Block::computeHmac(reinterpret_cast<const unsigned char*>(decodedServerKey.c_str()),
+ decodedServerKey.size(),
+ reinterpret_cast<const unsigned char*>(_authMessage.c_str()),
+ _authMessage.size());
StringBuilder sb;
- sb << "v=" << scram::hashToBase64(serverSignature);
+ sb << "v=" << serverSignature.toString();
*outputData = sb.str();
return StatusWith<bool>(false);