summaryrefslogtreecommitdiff
path: root/src/mongo/crypto
diff options
context:
space:
mode:
authorAndreas Nilsson <andreas.nilsson@10gen.com>2014-09-19 13:38:30 -0700
committerAndreas Nilsson <andreas.nilsson@10gen.com>2014-09-22 09:26:14 -0700
commit5376c8ad356acc9e43e0b5e1c6cda995ea209ddf (patch)
treeb4c99ad1d9eb17aa453f0f33d793643c3834d8d8 /src/mongo/crypto
parent881b3a97fb5080b4e5d5ce11ad016da73ea23931 (diff)
downloadmongo-5376c8ad356acc9e43e0b5e1c6cda995ea209ddf.tar.gz
SERVER-7596 Make SCRAM mechanism code independent db/
Diffstat (limited to 'src/mongo/crypto')
-rw-r--r--src/mongo/crypto/SConscript7
-rw-r--r--src/mongo/crypto/mechanism_scram.cpp273
-rw-r--r--src/mongo/crypto/mechanism_scram.h70
3 files changed, 350 insertions, 0 deletions
diff --git a/src/mongo/crypto/SConscript b/src/mongo/crypto/SConscript
new file mode 100644
index 00000000000..e021835b6da
--- /dev/null
+++ b/src/mongo/crypto/SConscript
@@ -0,0 +1,7 @@
+# -*- mode: python -*-
+
+Import("env")
+env.Library('scramauth',
+ ['mechanism_scram.cpp'],
+ LIBDEPS=['$BUILD_DIR/mongo/base/base',
+ '$BUILD_DIR/mongo/stringutils'])
diff --git a/src/mongo/crypto/mechanism_scram.cpp b/src/mongo/crypto/mechanism_scram.cpp
new file mode 100644
index 00000000000..42dcbc9e1b9
--- /dev/null
+++ b/src/mongo/crypto/mechanism_scram.cpp
@@ -0,0 +1,273 @@
+/*
+ * 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.
+ */
+
+#include "mongo/platform/basic.h"
+
+#include "mongo/crypto/mechanism_scram.h"
+
+#include <vector>
+
+#ifdef MONGO_SSL
+#include <openssl/sha.h>
+#include <openssl/evp.h>
+#include <openssl/hmac.h>
+#endif
+
+#include "mongo/platform/random.h"
+#include "mongo/util/base64.h"
+
+namespace mongo {
+namespace scram {
+
+// Need to #ifdef this until our SCRAM implementation
+// is independent of libcrypto
+#ifdef MONGO_SSL
+ // Compute the SCRAM step Hi() as defined in RFC5802
+ static void HMACIteration(const unsigned char input[],
+ size_t inputLen,
+ const unsigned char salt[],
+ size_t saltLen,
+ unsigned int iterationCount,
+ unsigned char output[]){
+ unsigned char intermediateDigest[hashSize];
+ unsigned char startKey[hashSize];
+ // Placeholder for HMAC return size, will always be scram::hashSize for HMAC SHA-1
+ unsigned int hashLen = 0;
+
+ uassert(17450, "invalid salt length provided", saltLen + 4 == hashSize);
+ memcpy (startKey, salt, saltLen);
+
+ startKey[saltLen] = 0;
+ startKey[saltLen+1] = 0;
+ startKey[saltLen+2] = 0;
+ startKey[saltLen+3] = 1;
+
+ // U1 = HMAC(input, salt + 0001)
+ fassert(17494, HMAC(EVP_sha1(),
+ input,
+ inputLen,
+ startKey,
+ saltLen + 4,
+ output,
+ &hashLen));
+
+ memcpy(intermediateDigest, output, hashSize);
+
+ // intermediateDigest contains Ui and output contains the accumulated XOR:ed result
+ for (size_t i = 2; i <= iterationCount; i++) {
+ fassert(17495, HMAC(EVP_sha1(),
+ input,
+ inputLen,
+ intermediateDigest,
+ hashSize,
+ intermediateDigest,
+ &hashLen));
+
+ for (size_t k = 0; k < hashSize; k++) {
+ output[k] ^= intermediateDigest[k];
+ }
+ }
+ }
+
+ // Iterate the hash function to generate SaltedPassword
+ void generateSaltedPassword(const StringData& password,
+ const unsigned char* salt,
+ const int saltLen,
+ const int iterationCount,
+ unsigned char saltedPassword[hashSize]) {
+ // saltedPassword = Hi(password, salt)
+ HMACIteration(reinterpret_cast<const unsigned char*>(password.rawData()),
+ password.size(),
+ salt,
+ saltLen,
+ iterationCount,
+ saltedPassword);
+ }
+
+ /* Compute the SCRAM secrets storedKey and serverKey
+ * as defined in RFC5802 */
+ static void computeProperties(const std::string& password,
+ const unsigned char salt[],
+ size_t saltLen,
+ size_t iterationCount,
+ unsigned char storedKey[hashSize],
+ unsigned char serverKey[hashSize]) {
+
+ unsigned char saltedPassword[hashSize];
+ unsigned char clientKey[hashSize];
+ unsigned int hashLen = 0;
+
+ generateSaltedPassword(password,
+ salt,
+ saltLen,
+ iterationCount,
+ saltedPassword);
+
+ // clientKey = HMAC(saltedPassword, "Client Key")
+ fassert(17498, HMAC(EVP_sha1(),
+ saltedPassword,
+ hashSize,
+ reinterpret_cast<const unsigned char*>(clientKeyConst.data()),
+ clientKeyConst.size(),
+ clientKey,
+ &hashLen));
+
+ // storedKey = H(clientKey)
+ fassert(17499, SHA1(clientKey, hashSize, storedKey));
+
+ // serverKey = HMAC(saltedPassword, "Server Key")
+ fassert(17500, HMAC(EVP_sha1(),
+ saltedPassword,
+ hashSize,
+ reinterpret_cast<const unsigned char*>(serverKeyConst.data()),
+ serverKeyConst.size(),
+ serverKey,
+ &hashLen));
+ }
+
+#endif //MONGO_SSL
+
+ BSONObj generateCredentials(const std::string& hashedPassword) {
+#ifndef MONGO_SSL
+ return BSONObj();
+#else
+
+ // TODO: configure the default iteration count via setParameter
+ const int iterationCount = 10000;
+ const int saltLenQWords = 2;
+
+ // Generate salt
+ uint64_t userSalt[saltLenQWords];
+
+ scoped_ptr<SecureRandom> sr(SecureRandom::create());
+
+ userSalt[0] = sr->nextInt64();
+ userSalt[1] = sr->nextInt64();
+ std::string encodedUserSalt =
+ base64::encode(reinterpret_cast<char*>(userSalt), sizeof(userSalt));
+
+ // Compute SCRAM secrets serverKey and storedKey
+ unsigned char storedKey[hashSize];
+ unsigned char serverKey[hashSize];
+
+ computeProperties(hashedPassword,
+ reinterpret_cast<unsigned char*>(userSalt),
+ saltLenQWords*sizeof(uint64_t),
+ iterationCount,
+ storedKey,
+ serverKey);
+
+ std::string encodedStoredKey =
+ base64::encode(reinterpret_cast<char*>(storedKey), hashSize);
+ std::string encodedServerKey =
+ base64::encode(reinterpret_cast<char*>(serverKey), hashSize);
+
+ return BSON("iterationCount" << iterationCount <<
+ "salt" << encodedUserSalt <<
+ "storedKey" << encodedStoredKey <<
+ "serverKey" << encodedServerKey);
+#endif
+ }
+
+ std::string generateClientProof(const unsigned char saltedPassword[hashSize],
+ const std::string& authMessage) {
+#ifndef MONGO_SSL
+ return "";
+#else
+
+ // ClientKey := HMAC(saltedPassword, "Client Key")
+ unsigned char clientKey[hashSize];
+ unsigned int hashLen = 0;
+ fassert(18689, HMAC(EVP_sha1(),
+ saltedPassword,
+ hashSize,
+ reinterpret_cast<const unsigned char*>(clientKeyConst.data()),
+ clientKeyConst.size(),
+ clientKey,
+ &hashLen));
+
+ // StoredKey := H(clientKey)
+ unsigned char storedKey[hashSize];
+ fassert(18701, SHA1(clientKey, hashSize, storedKey));
+
+ // ClientSignature := HMAC(StoredKey, AuthMessage)
+ unsigned char clientSignature[hashSize];
+ fassert(18702, HMAC(EVP_sha1(),
+ storedKey,
+ hashSize,
+ reinterpret_cast<const unsigned char*>(authMessage.c_str()),
+ authMessage.size(),
+ clientSignature,
+ &hashLen));
+
+ // ClientProof := ClientKey XOR ClientSignature
+ unsigned char clientProof[hashSize];
+ for (size_t i = 0; i<hashSize; i++) {
+ clientProof[i] = clientKey[i] ^ clientSignature[i];
+ }
+
+ return base64::encode(reinterpret_cast<char*>(clientProof), hashSize);
+
+#endif // MONGO_SSL
+ }
+
+ bool verifyServerSignature(const unsigned char saltedPassword[hashSize],
+ const std::string& authMessage,
+ const std::string& receivedServerSignature) {
+#ifndef MONGO_SSL
+ return false;
+#else
+ // ServerKey := HMAC(SaltedPassword, "Server Key")
+ unsigned int hashLen;
+ unsigned char serverKey[hashSize];
+ fassert(18703, HMAC(EVP_sha1(),
+ saltedPassword,
+ hashSize,
+ reinterpret_cast<const unsigned char*>(serverKeyConst.data()),
+ serverKeyConst.size(),
+ serverKey,
+ &hashLen));
+
+ // ServerSignature := HMAC(ServerKey, AuthMessage)
+ unsigned char serverSignature[hashSize];
+ fassert(18704, HMAC(EVP_sha1(),
+ serverKey,
+ hashSize,
+ reinterpret_cast<const unsigned char*>(authMessage.c_str()),
+ authMessage.size(),
+ serverSignature,
+ &hashLen));
+
+ std::string encodedServerSignature =
+ base64::encode(reinterpret_cast<char*>(serverSignature), sizeof(serverSignature));
+ return (receivedServerSignature == encodedServerSignature);
+#endif
+ }
+
+} // namespace scram
+} // namespace mongo
diff --git a/src/mongo/crypto/mechanism_scram.h b/src/mongo/crypto/mechanism_scram.h
new file mode 100644
index 00000000000..451f13a60d8
--- /dev/null
+++ b/src/mongo/crypto/mechanism_scram.h
@@ -0,0 +1,70 @@
+/**
+* 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 <string>
+
+#include "mongo/base/status.h"
+#include "mongo/db/jsobj.h"
+
+namespace mongo {
+namespace scram {
+ const unsigned int hashSize = 20;
+ const std::string serverKeyConst = "Server Key";
+ const std::string clientKeyConst = "Client Key";
+
+ /*
+ * Computes the SaltedPassword from password, salt and iterationCount.
+ */
+ void generateSaltedPassword(const StringData& password,
+ const unsigned char* salt,
+ const int saltLen,
+ const int iterationCount,
+ unsigned char saltedPassword[hashSize]);
+
+ /*
+ * Generates the user salt and the SCRAM secrets storedKey and serverKey as
+ * defined in RFC5802 (server side).
+ */
+ BSONObj generateCredentials(const std::string& hashedPassword);
+
+ /*
+ * Computes the ClientProof from SaltedPassword and authMessage (client side).
+ */
+ std::string generateClientProof(const unsigned char saltedPassword[hashSize],
+ const std::string& authMessage);
+
+ /*
+ * Verifies ServerSignature (client side).
+ */
+ bool verifyServerSignature(const unsigned char saltedPassword[hashSize],
+ const std::string& authMessage,
+ const std::string& serverSignature);
+} // namespace scram
+} // namespace mongo