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 /src/mongo/db | |
parent | 8c173ff0776c2c4ab1698a26aee2d087f973a3de (diff) | |
download | mongo-0d408153594e2e2366e0729397ca2890f00b026c.tar.gz |
SERVER-27768 Implement HMAC key for signing Logical clock's storage & distribution
Diffstat (limited to 'src/mongo/db')
-rw-r--r-- | src/mongo/db/SConscript | 23 | ||||
-rw-r--r-- | src/mongo/db/db.cpp | 4 | ||||
-rw-r--r-- | src/mongo/db/logical_clock_test.cpp | 8 | ||||
-rw-r--r-- | src/mongo/db/logical_clock_test_fixture.cpp | 4 | ||||
-rw-r--r-- | src/mongo/db/logical_time.cpp | 8 | ||||
-rw-r--r-- | src/mongo/db/logical_time.h | 6 | ||||
-rw-r--r-- | src/mongo/db/logical_time_test.cpp | 17 | ||||
-rw-r--r-- | src/mongo/db/repl/replication_coordinator_external_state_impl.cpp | 4 | ||||
-rw-r--r-- | src/mongo/db/repl/replication_coordinator_test_fixture.cpp | 4 | ||||
-rw-r--r-- | src/mongo/db/service_context_d_test_fixture.cpp | 4 | ||||
-rw-r--r-- | src/mongo/db/time_proof_service.cpp | 65 | ||||
-rw-r--r-- | src/mongo/db/time_proof_service.h | 29 | ||||
-rw-r--r-- | src/mongo/db/time_proof_service_test.cpp | 66 |
13 files changed, 225 insertions, 17 deletions
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 |