diff options
author | Jack Mulrow <jack.mulrow@mongodb.com> | 2017-02-03 14:37:27 -0500 |
---|---|---|
committer | Jack Mulrow <jack.mulrow@mongodb.com> | 2017-03-02 12:24:37 -0500 |
commit | 0d408153594e2e2366e0729397ca2890f00b026c (patch) | |
tree | 5201e5e9fd707447543bf8c638b0a701f314ca5e | |
parent | 8c173ff0776c2c4ab1698a26aee2d087f973a3de (diff) | |
download | mongo-0d408153594e2e2366e0729397ca2890f00b026c.tar.gz |
SERVER-27768 Implement HMAC key for signing Logical clock's storage & distribution
23 files changed, 349 insertions, 48 deletions
diff --git a/src/mongo/base/error_codes.err b/src/mongo/base/error_codes.err index 9ed2726b793..c5c6d0cc021 100644 --- a/src/mongo/base/error_codes.err +++ b/src/mongo/base/error_codes.err @@ -201,6 +201,7 @@ error_code("ChunkRangeCleanupPending", 200) error_code("CannotBuildIndexKeys", 201) error_code("NetworkInterfaceExceededTimeLimit", 202) error_code("ShardingStateNotInitialized", 203) +error_code("TimeProofMismatch", 204) # Non-sequential error codes (for compatibility only) error_code("SocketException", 9001) diff --git a/src/mongo/crypto/SConscript b/src/mongo/crypto/SConscript index a18405fbce5..b7f06827201 100644 --- a/src/mongo/crypto/SConscript +++ b/src/mongo/crypto/SConscript @@ -19,6 +19,7 @@ env.Library('sha1_block', ], LIBDEPS=[ '$BUILD_DIR/mongo/base', + '$BUILD_DIR/mongo/util/secure_compare_memory', ]) env.Library( @@ -45,6 +46,7 @@ env.Library('scramauth', ['mechanism_scram.cpp'], LIBDEPS=['$BUILD_DIR/mongo/base', '$BUILD_DIR/mongo/base/secure_allocator', + '$BUILD_DIR/mongo/util/secure_compare_memory', '$BUILD_DIR/mongo/util/secure_zero_memory', 'sha1_block_${MONGO_CRYPTO}']) diff --git a/src/mongo/crypto/mechanism_scram.cpp b/src/mongo/crypto/mechanism_scram.cpp index a1b3c69d8fc..b426dda6cbd 100644 --- a/src/mongo/crypto/mechanism_scram.cpp +++ b/src/mongo/crypto/mechanism_scram.cpp @@ -34,6 +34,7 @@ #include "mongo/platform/random.h" #include "mongo/util/base64.h" +#include "mongo/util/secure_compare_memory.h" #include "mongo/util/secure_zero_memory.h" namespace mongo { @@ -41,31 +42,6 @@ namespace scram { using std::unique_ptr; -namespace { -/** - * Compare two arrays of bytes for equality in constant time. - * - * This means that the function runs for the same amount of time even if they differ. Unlike memcmp, - * this function does not exit on the first difference. - * - * Returns true if the two arrays are equal. - * - * TODO: evaluate if LTO inlines or changes the code flow of this function. - */ -NOINLINE_DECL -bool consttimeMemEqual(volatile const unsigned char* s1, // NOLINT - using volatile to - volatile const unsigned char* s2, // NOLINT - disable compiler optimizations - size_t length) { - unsigned int ret = 0; - - for (size_t i = 0; i < length; ++i) { - ret |= s1[i] ^ s2[i]; - } - - return (1 & ((ret - 1) >> 8)); -} -} // namespace - // Compute the SCRAM step Hi() as defined in RFC5802 static SHA1Block HMACIteration(const unsigned char input[], size_t inputLen, diff --git a/src/mongo/crypto/sha1_block.cpp b/src/mongo/crypto/sha1_block.cpp index c978e545b20..9debee81fcc 100644 --- a/src/mongo/crypto/sha1_block.cpp +++ b/src/mongo/crypto/sha1_block.cpp @@ -34,6 +34,7 @@ #include "mongo/bson/bsonobjbuilder.h" #include "mongo/util/base64.h" #include "mongo/util/mongoutils/str.h" +#include "mongo/util/secure_compare_memory.h" namespace mongo { @@ -81,8 +82,12 @@ void SHA1Block::xorInline(const SHA1Block& other) { } } -bool SHA1Block::operator==(const SHA1Block& rhs) const { - return rhs._hash == this->_hash; +bool SHA1Block::operator==(const SHA1Block& other) const { + return consttimeMemEqual(this->_hash.data(), other._hash.data(), kHashLength); +} + +bool SHA1Block::operator!=(const SHA1Block& other) const { + return !(*this == other); } } // namespace mongo diff --git a/src/mongo/crypto/sha1_block.h b/src/mongo/crypto/sha1_block.h index 3398cea6018..a228540bcb0 100644 --- a/src/mongo/crypto/sha1_block.h +++ b/src/mongo/crypto/sha1_block.h @@ -90,7 +90,8 @@ public: void xorInline(const SHA1Block& other); std::string toString() const; - bool operator==(const SHA1Block& rhs) const; + bool operator==(const SHA1Block& other) const; + bool operator!=(const SHA1Block& other) const; private: HashType _hash; diff --git a/src/mongo/db/SConscript b/src/mongo/db/SConscript index 2f085cdb6aa..7a43c188625 100644 --- a/src/mongo/db/SConscript +++ b/src/mongo/db/SConscript @@ -945,6 +945,17 @@ env.Library( ) env.Library( + target='time_proof_service', + source=[ + 'time_proof_service.cpp', + ], + LIBDEPS=[ + '$BUILD_DIR/mongo/crypto/sha1_block_${MONGO_CRYPTO}', + 'logical_time', + ], +) + +env.Library( target='logical_clock', source=[ 'logical_clock.cpp', @@ -952,6 +963,7 @@ env.Library( LIBDEPS=[ 'service_context', 'signed_logical_time', + 'time_proof_service', ], ) @@ -963,6 +975,7 @@ env.CppUnitTest( LIBDEPS=[ 'logical_time', 'signed_logical_time', + 'time_proof_service', ], ) @@ -991,6 +1004,16 @@ env.Library( ], ) +env.CppUnitTest( + target='time_proof_service_test', + source=[ + 'time_proof_service_test.cpp', + ], + LIBDEPS=[ + 'time_proof_service', + ], +) + env.Library( target= 'op_observer_noop', source= [ diff --git a/src/mongo/db/db.cpp b/src/mongo/db/db.cpp index 251e08824a3..6dae80fbbb0 100644 --- a/src/mongo/db/db.cpp +++ b/src/mongo/db/db.cpp @@ -894,7 +894,9 @@ MONGO_INITIALIZER_WITH_PREREQUISITES(CreateReplicationManager, topoCoordOptions.maxSyncSourceLagSecs = Seconds(repl::maxSyncSourceLagSecs); topoCoordOptions.clusterRole = serverGlobalParams.clusterRole; - auto timeProofService = stdx::make_unique<TimeProofService>(); + std::array<std::uint8_t, 20> tempKey = {}; + TimeProofService::Key key(std::move(tempKey)); + auto timeProofService = stdx::make_unique<TimeProofService>(std::move(key)); auto logicalClock = stdx::make_unique<LogicalClock>(serviceContext, std::move(timeProofService), false); LogicalClock::set(serviceContext, std::move(logicalClock)); diff --git a/src/mongo/db/logical_clock_test.cpp b/src/mongo/db/logical_clock_test.cpp index 729edaf650c..e3e75acd9dc 100644 --- a/src/mongo/db/logical_clock_test.cpp +++ b/src/mongo/db/logical_clock_test.cpp @@ -46,7 +46,9 @@ class LogicalClockTestBase : public unittest::Test { protected: void setUp() { _serviceContext = stdx::make_unique<ServiceContextNoop>(); - auto pTps = stdx::make_unique<TimeProofService>(); + std::array<std::uint8_t, 20> tempKey = {}; + TimeProofService::Key key(std::move(tempKey)); + auto pTps = stdx::make_unique<TimeProofService>(std::move(key)); _timeProofService = pTps.get(); _clock = stdx::make_unique<LogicalClock>(_serviceContext.get(), std::move(pTps), true); } @@ -75,7 +77,9 @@ TEST_F(LogicalClockTestBase, roundtrip) { // Create different logicalClock instance to validate that the initial time is preserved. ServiceContextNoop serviceContext; Timestamp tX(1); - auto pTps = stdx::make_unique<TimeProofService>(); + std::array<std::uint8_t, 20> tempKey = {}; + TimeProofService::Key key(std::move(tempKey)); + auto pTps = stdx::make_unique<TimeProofService>(std::move(key)); auto time = LogicalTime(tX); LogicalClock logicalClock(&serviceContext, std::move(pTps), true); diff --git a/src/mongo/db/logical_clock_test_fixture.cpp b/src/mongo/db/logical_clock_test_fixture.cpp index 64cf8182f22..b0001ebab90 100644 --- a/src/mongo/db/logical_clock_test_fixture.cpp +++ b/src/mongo/db/logical_clock_test_fixture.cpp @@ -40,7 +40,9 @@ namespace mongo { void LogicalClockTest::setUp() { auto service = getGlobalServiceContext(); - auto timeProofService = stdx::make_unique<TimeProofService>(); + std::array<std::uint8_t, 20> tempKey = {}; + TimeProofService::Key key(std::move(tempKey)); + auto timeProofService = stdx::make_unique<TimeProofService>(std::move(key)); auto logicalClock = stdx::make_unique<LogicalClock>(service, std::move(timeProofService), false); LogicalClock::set(service, std::move(logicalClock)); diff --git a/src/mongo/db/logical_time.cpp b/src/mongo/db/logical_time.cpp index e9e5b4c5b34..c26d542ec3d 100644 --- a/src/mongo/db/logical_time.cpp +++ b/src/mongo/db/logical_time.cpp @@ -28,6 +28,8 @@ #include "mongo/db/logical_time.h" +#include "mongo/base/data_type_endian.h" +#include "mongo/base/data_view.h" #include "mongo/platform/basic.h" #include "mongo/util/mongoutils/str.h" @@ -51,4 +53,10 @@ std::string LogicalTime::toString() const { return buf.str(); } +std::array<unsigned char, sizeof(uint64_t)> LogicalTime::toUnsignedArray() const { + std::array<unsigned char, sizeof(uint64_t)> output; + DataView(reinterpret_cast<char*>(output.data())).write(LittleEndian<uint64_t>{_time}); + return output; +} + } // namespace mongo diff --git a/src/mongo/db/logical_time.h b/src/mongo/db/logical_time.h index 2bc43bf54d4..4b982612183 100644 --- a/src/mongo/db/logical_time.h +++ b/src/mongo/db/logical_time.h @@ -59,6 +59,12 @@ public: std::string toString() const; /** + * Returns the LogicalTime as an array of unsigned chars in little endian order for use with the + * crypto::hmacSHA1 function. + */ + std::array<unsigned char, sizeof(uint64_t)> toUnsignedArray() const; + + /** * An uninitialized value of LogicalTime. Default constructed. */ static const LogicalTime kUninitialized; diff --git a/src/mongo/db/logical_time_test.cpp b/src/mongo/db/logical_time_test.cpp index e7d7d8e2819..f0f0d7b4477 100644 --- a/src/mongo/db/logical_time_test.cpp +++ b/src/mongo/db/logical_time_test.cpp @@ -92,9 +92,24 @@ TEST(LogicalTime, defaultInit) { ASSERT_TRUE(tX == lT.asTimestamp()); } +TEST(LogicalTime, toUnsignedArray) { + Timestamp tX(123456789); + auto lT = LogicalTime(tX); + + unsigned char expectedBytes[sizeof(uint64_t)] = { + 0x15, 0xCD, 0x5B, 0x07, 0x00, 0x00, 0x00, 0x00}; + + auto unsignedTimeArray = lT.toUnsignedArray(); + for (size_t i = 0; i < sizeof(uint64_t); ++i) { + ASSERT_EQUALS(unsignedTimeArray[i], expectedBytes[i]); + } +} + TEST(SignedLogicalTime, roundtrip) { Timestamp tX(1); - TimeProofService tps; + std::array<std::uint8_t, 20> tempKey = {}; + TimeProofService::Key key(std::move(tempKey)); + TimeProofService tps(std::move(key)); auto time = LogicalTime(tX); auto proof = tps.getProof(time); diff --git a/src/mongo/db/repl/replication_coordinator_external_state_impl.cpp b/src/mongo/db/repl/replication_coordinator_external_state_impl.cpp index f5a96ed3123..70c74cc8942 100644 --- a/src/mongo/db/repl/replication_coordinator_external_state_impl.cpp +++ b/src/mongo/db/repl/replication_coordinator_external_state_impl.cpp @@ -785,6 +785,10 @@ void ReplicationCoordinatorExternalStateImpl::_shardingOnTransitionToPrimaryHook // If this is a config server node becoming a primary, start the balancer Balancer::get(txn)->initiateBalancer(txn); + + // Generate and upsert random 20 byte key for the LogicalClock's TimeProofService. + // TODO: SERVER-27768 + } else if (ShardingState::get(txn)->enabled()) { const auto configsvrConnStr = Grid::get(txn)->shardRegistry()->getConfigShard()->getConnString(); diff --git a/src/mongo/db/repl/replication_coordinator_test_fixture.cpp b/src/mongo/db/repl/replication_coordinator_test_fixture.cpp index 24ecd3af4d0..8140513c139 100644 --- a/src/mongo/db/repl/replication_coordinator_test_fixture.cpp +++ b/src/mongo/db/repl/replication_coordinator_test_fixture.cpp @@ -122,7 +122,9 @@ void ReplCoordTest::init() { // PRNG seed for tests. const int64_t seed = 0; - auto timeProofService = stdx::make_unique<TimeProofService>(); + std::array<std::uint8_t, 20> tempKey = {}; + TimeProofService::Key key(std::move(tempKey)); + auto timeProofService = stdx::make_unique<TimeProofService>(std::move(key)); auto logicalClock = stdx::make_unique<LogicalClock>(service, std::move(timeProofService), false); LogicalClock::set(service, std::move(logicalClock)); diff --git a/src/mongo/db/service_context_d_test_fixture.cpp b/src/mongo/db/service_context_d_test_fixture.cpp index 9d6087e066e..238f0936a0a 100644 --- a/src/mongo/db/service_context_d_test_fixture.cpp +++ b/src/mongo/db/service_context_d_test_fixture.cpp @@ -54,7 +54,9 @@ void ServiceContextMongoDTest::setUp() { Client::initThread(getThreadName().c_str()); ServiceContext* serviceContext = getServiceContext(); - auto timeProofService = stdx::make_unique<TimeProofService>(); + std::array<std::uint8_t, 20> tempKey = {}; + TimeProofService::Key key(std::move(tempKey)); + auto timeProofService = stdx::make_unique<TimeProofService>(std::move(key)); auto logicalClock = stdx::make_unique<LogicalClock>(serviceContext, std::move(timeProofService), false); LogicalClock::set(serviceContext, std::move(logicalClock)); diff --git a/src/mongo/db/time_proof_service.cpp b/src/mongo/db/time_proof_service.cpp new file mode 100644 index 00000000000..1d0d6c0b46b --- /dev/null +++ b/src/mongo/db/time_proof_service.cpp @@ -0,0 +1,65 @@ +/** + * 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/db/time_proof_service.h" + +#include "mongo/base/status.h" +#include "mongo/db/logical_time.h" +#include "mongo/platform/random.h" + +namespace mongo { + +TimeProofService::Key TimeProofService::generateRandomKey() { + // SecureRandom only produces 64-bit numbers, so 3 is the minimum for 20 random bytes. + const size_t kRandomNumbers = 3; + std::array<std::int64_t, kRandomNumbers> keyBuffer; + std::unique_ptr<SecureRandom> rng(SecureRandom::create()); + std::generate(keyBuffer.begin(), keyBuffer.end(), [&] { return rng->nextInt64(); }); + + return fassertStatusOK(40384, + SHA1Block::fromBuffer(reinterpret_cast<std::uint8_t*>(keyBuffer.data()), + SHA1Block::kHashLength)); +} + +TimeProofService::TimeProof TimeProofService::getProof(const LogicalTime& time) const { + auto unsignedTimeArray = time.toUnsignedArray(); + return SHA1Block::computeHmac( + _key.data(), _key.size(), unsignedTimeArray.data(), unsignedTimeArray.size()); +} + +Status TimeProofService::checkProof(const LogicalTime& time, const TimeProof& proof) const { + auto myProof = getProof(time); + if (myProof != proof) { + return Status(ErrorCodes::TimeProofMismatch, "Proof does not match the logical time"); + } + return Status::OK(); +} + +} // namespace mongo diff --git a/src/mongo/db/time_proof_service.h b/src/mongo/db/time_proof_service.h index 5be6bf46e38..2157523f6fc 100644 --- a/src/mongo/db/time_proof_service.h +++ b/src/mongo/db/time_proof_service.h @@ -35,27 +35,36 @@ namespace mongo { /** - * Mock of the TimeProofService class. The class when fully implemented will be self-contained. It - * will provide key management and rotation, caching and other optimizations as needed. + * TODO: SERVER-28127 Add key rotation to the TimeProofService + * + * The TimeProofService holds the key used by mongod and mongos processes to verify logical times + * and contains the logic to generate this key, but not to store or retrieve it. */ class TimeProofService { public: // This type must be synchronized with the library that generates SHA1 or other proof. using TimeProof = SHA1Block; + using Key = SHA1Block; + + TimeProofService(Key key) : _key(std::move(key)) {} + + /** + * Generates a pseudorandom key to be used for HMAC authentication. + */ + static Key generateRandomKey(); /** - * Returns the proof matching the time argument. + * Returns the proof matching the time argument. */ - TimeProof getProof(LogicalTime time) { - return SHA1Block(); - } + TimeProof getProof(const LogicalTime& time) const; /** - * Verifies that the proof is matching the time argument. + * Verifies that the proof matches the time argument. */ - Status checkProof(LogicalTime time, const TimeProof& proof) { - return Status::OK(); - } + Status checkProof(const LogicalTime& time, const TimeProof& proof) const; + +private: + Key _key; }; } // namespace mongo diff --git a/src/mongo/db/time_proof_service_test.cpp b/src/mongo/db/time_proof_service_test.cpp new file mode 100644 index 00000000000..6c4a8d51256 --- /dev/null +++ b/src/mongo/db/time_proof_service_test.cpp @@ -0,0 +1,66 @@ +/** + * 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/bson/timestamp.h" +#include "mongo/db/logical_time.h" +#include "mongo/db/time_proof_service.h" +#include "mongo/unittest/unittest.h" + +namespace mongo { +namespace { + +using TimeProof = TimeProofService::TimeProof; + +// Verifies logical time with proof signed with the correct key. +TEST(TimeProofService, VerifyLogicalTimeWithValidProof) { + std::array<std::uint8_t, 20> tempKey = {}; + TimeProofService::Key key(std::move(tempKey)); + TimeProofService timeProofService(std::move(key)); + + LogicalTime time(Timestamp(1)); + TimeProof proof = timeProofService.getProof(time); + + ASSERT_OK(timeProofService.checkProof(time, proof)); +} + +// Fails for logical time with proof signed with an invalid key. +TEST(TimeProofService, LogicalTimeWithMismatchingProofShouldFail) { + std::array<std::uint8_t, 20> tempKey = {}; + TimeProofService::Key key(std::move(tempKey)); + TimeProofService timeProofService(std::move(key)); + + LogicalTime time(Timestamp(1)); + TimeProof invalidProof = {{1, 2, 3}}; + + ASSERT_EQUALS(ErrorCodes::TimeProofMismatch, timeProofService.checkProof(time, invalidProof)); +} + +} // unnamed namespace +} // namespace mongo diff --git a/src/mongo/dbtests/dbtests.cpp b/src/mongo/dbtests/dbtests.cpp index 12cb6ff9d06..75883c53091 100644 --- a/src/mongo/dbtests/dbtests.cpp +++ b/src/mongo/dbtests/dbtests.cpp @@ -132,7 +132,9 @@ int dbtestsMain(int argc, char** argv, char** envp) { replSettings.setOplogSizeBytes(10 * 1024 * 1024); ServiceContext* service = getGlobalServiceContext(); - auto timeProofService = stdx::make_unique<TimeProofService>(); + std::array<std::uint8_t, 20> tempKey = {}; + TimeProofService::Key key(std::move(tempKey)); + auto timeProofService = stdx::make_unique<TimeProofService>(std::move(key)); auto logicalClock = stdx::make_unique<LogicalClock>(service, std::move(timeProofService), false); LogicalClock::set(service, std::move(logicalClock)); diff --git a/src/mongo/s/server.cpp b/src/mongo/s/server.cpp index 60b46736290..2fab9570572 100644 --- a/src/mongo/s/server.cpp +++ b/src/mongo/s/server.cpp @@ -277,9 +277,13 @@ static ExitCode runMongosServer() { auto opCtx = cc().makeOperationContext(); - auto timeProofService = stdx::make_unique<TimeProofService>(); + std::array<std::uint8_t, 20> tempKey = {}; + TimeProofService::Key key(std::move(tempKey)); + auto timeProofService = stdx::make_unique<TimeProofService>(std::move(key)); auto logicalClock = stdx::make_unique<LogicalClock>( - opCtx->getServiceContext(), std::move(timeProofService), false); + opCtx->getServiceContext(), + std::move(timeProofService), + serverGlobalParams.authState == ServerGlobalParams::AuthState::kEnabled); LogicalClock::set(opCtx->getServiceContext(), std::move(logicalClock)); { diff --git a/src/mongo/util/SConscript b/src/mongo/util/SConscript index c277fb4a1e8..a5b2a65c8cf 100644 --- a/src/mongo/util/SConscript +++ b/src/mongo/util/SConscript @@ -396,6 +396,15 @@ quick_exit_env.Library( "$BUILD_DIR/third_party/shim_allocator", ] ) +env.Library( + target="secure_compare_memory", + source=[ + 'secure_compare_memory.cpp', + ], + LIBDEPS=[ + "$BUILD_DIR/mongo/base", + ], +) env.Library( target="secure_zero_memory", diff --git a/src/mongo/util/secure_compare_memory.cpp b/src/mongo/util/secure_compare_memory.cpp new file mode 100644 index 00000000000..1f22de56433 --- /dev/null +++ b/src/mongo/util/secure_compare_memory.cpp @@ -0,0 +1,47 @@ +/* Copyright 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/util/secure_compare_memory.h" + +namespace mongo { + +bool consttimeMemEqual(volatile const unsigned char* s1, // NOLINT - using volatile to + volatile const unsigned char* s2, // NOLINT - disable compiler optimizations + size_t length) { + unsigned int ret = 0; + + for (size_t i = 0; i < length; ++i) { + ret |= s1[i] ^ s2[i]; + } + + return (1 & ((ret - 1) >> 8)); +} + +} // namespace mongo diff --git a/src/mongo/util/secure_compare_memory.h b/src/mongo/util/secure_compare_memory.h new file mode 100644 index 00000000000..7895fc4a5ba --- /dev/null +++ b/src/mongo/util/secure_compare_memory.h @@ -0,0 +1,46 @@ +/* Copyright 2017 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 <cstddef> + +namespace mongo { + +/** + * Compare two arrays of bytes for equality in constant time. + * + * This means that the function runs for the same amount of time even if they differ. Unlike memcmp, + * this function does not exit on the first difference. + * + * Returns true if the two arrays are equal. + */ +bool consttimeMemEqual(volatile const unsigned char* s1, // NOLINT - using volatile to + volatile const unsigned char* s2, // NOLINT - disable compiler optimizations + size_t length); + +} // namespace mongo |