diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/mongo/crypto/sha1_block.cpp | 2 | ||||
-rw-r--r-- | src/mongo/crypto/sha1_block.h | 2 | ||||
-rw-r--r-- | src/mongo/db/SConscript | 1 | ||||
-rw-r--r-- | src/mongo/db/signed_logical_time.cpp | 2 | ||||
-rw-r--r-- | src/mongo/db/signed_logical_time.h | 2 | ||||
-rw-r--r-- | src/mongo/db/time_proof_service.h | 6 | ||||
-rw-r--r-- | src/mongo/rpc/SConscript | 3 | ||||
-rw-r--r-- | src/mongo/rpc/metadata/logical_time_metadata.cpp | 94 | ||||
-rw-r--r-- | src/mongo/rpc/metadata/logical_time_metadata.h | 68 | ||||
-rw-r--r-- | src/mongo/rpc/metadata/logical_time_metadata_test.cpp | 113 |
10 files changed, 286 insertions, 7 deletions
diff --git a/src/mongo/crypto/sha1_block.cpp b/src/mongo/crypto/sha1_block.cpp index 52880cd0438..c978e545b20 100644 --- a/src/mongo/crypto/sha1_block.cpp +++ b/src/mongo/crypto/sha1_block.cpp @@ -71,7 +71,7 @@ std::string SHA1Block::toString() const { return base64::encode(reinterpret_cast<const char*>(_hash.data()), _hash.size()); } -void SHA1Block::appendAsBinData(BSONObjBuilder& builder, StringData fieldName) { +void SHA1Block::appendAsBinData(BSONObjBuilder& builder, StringData fieldName) const { builder.appendBinData(fieldName, _hash.size(), BinDataGeneral, _hash.data()); } diff --git a/src/mongo/crypto/sha1_block.h b/src/mongo/crypto/sha1_block.h index 588ceb8da52..3398cea6018 100644 --- a/src/mongo/crypto/sha1_block.h +++ b/src/mongo/crypto/sha1_block.h @@ -81,7 +81,7 @@ public: /** * Append this to a builder using the given name as a BSON BinData type value. */ - void appendAsBinData(BSONObjBuilder& builder, StringData fieldName); + void appendAsBinData(BSONObjBuilder& builder, StringData fieldName) const; /** * Do a bitwise xor against another SHA1Block and replace the current contents of this block diff --git a/src/mongo/db/SConscript b/src/mongo/db/SConscript index 55ea57fe7fc..20aa7fe127e 100644 --- a/src/mongo/db/SConscript +++ b/src/mongo/db/SConscript @@ -941,6 +941,7 @@ env.Library( ], LIBDEPS=[ 'logical_time', + '$BUILD_DIR/mongo/crypto/sha1_block', ], ) diff --git a/src/mongo/db/signed_logical_time.cpp b/src/mongo/db/signed_logical_time.cpp index bcfb89f027a..16404950ac5 100644 --- a/src/mongo/db/signed_logical_time.cpp +++ b/src/mongo/db/signed_logical_time.cpp @@ -34,7 +34,7 @@ namespace mongo { std::string SignedLogicalTime::toString() const { StringBuilder buf; - buf << _time.toString() << "|" << _proof; + buf << _time.toString() << "|" << _proof.toString(); return buf.str(); } diff --git a/src/mongo/db/signed_logical_time.h b/src/mongo/db/signed_logical_time.h index 52cf1230e58..7b96e51df79 100644 --- a/src/mongo/db/signed_logical_time.h +++ b/src/mongo/db/signed_logical_time.h @@ -36,7 +36,7 @@ namespace mongo { /** * The SignedLogicalTime class is a pair of value i.e. time and a signature i.e. _proof - * The class is immutable and is used to hold the cryptogrphically protected LogicalTime. + * The class is immutable and is used to hold the cryptographically protected LogicalTime. */ class SignedLogicalTime { public: diff --git a/src/mongo/db/time_proof_service.h b/src/mongo/db/time_proof_service.h index bc43e1ceee3..5be6bf46e38 100644 --- a/src/mongo/db/time_proof_service.h +++ b/src/mongo/db/time_proof_service.h @@ -29,6 +29,7 @@ #pragma once #include "mongo/base/status.h" +#include "mongo/crypto/sha1_block.h" #include "mongo/db/logical_time.h" namespace mongo { @@ -40,14 +41,13 @@ namespace mongo { class TimeProofService { public: // This type must be synchronized with the library that generates SHA1 or other proof. - using TimeProof = std::string; + using TimeProof = SHA1Block; /** * Returns the proof matching the time argument. */ TimeProof getProof(LogicalTime time) { - TimeProof proof = "12345678901234567890"; - return proof; + return SHA1Block(); } /** diff --git a/src/mongo/rpc/SConscript b/src/mongo/rpc/SConscript index 93764bdbd92..a4945dcf9c6 100644 --- a/src/mongo/rpc/SConscript +++ b/src/mongo/rpc/SConscript @@ -150,6 +150,7 @@ env.Clone().InjectModule("enterprise").Library( 'metadata/audit_metadata.cpp', 'metadata/config_server_metadata.cpp', 'metadata/egress_metadata_hook_list.cpp', + 'metadata/logical_time_metadata.cpp', 'metadata/server_selection_metadata.cpp', 'metadata/sharding_metadata.cpp', 'metadata/repl_set_metadata.cpp', @@ -162,6 +163,7 @@ env.Clone().InjectModule("enterprise").Library( '$BUILD_DIR/mongo/bson/util/bson_extract', '$BUILD_DIR/mongo/client/read_preference', '$BUILD_DIR/mongo/db/repl/optime', + '$BUILD_DIR/mongo/db/signed_logical_time', '$BUILD_DIR/mongo/util/decorable', ], ) @@ -172,6 +174,7 @@ env.CppUnitTest( ], source=[ 'metadata/egress_metadata_hook_list_test.cpp', + 'metadata/logical_time_metadata_test.cpp', 'metadata/server_selection_metadata_test.cpp', 'metadata/sharding_metadata_test.cpp', 'metadata/tracking_metadata_test.cpp', diff --git a/src/mongo/rpc/metadata/logical_time_metadata.cpp b/src/mongo/rpc/metadata/logical_time_metadata.cpp new file mode 100644 index 00000000000..ef056a2fd46 --- /dev/null +++ b/src/mongo/rpc/metadata/logical_time_metadata.cpp @@ -0,0 +1,94 @@ +/** + * 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/rpc/metadata/logical_time_metadata.h" + +#include "mongo/bson/bsonobjbuilder.h" +#include "mongo/bson/util/bson_extract.h" + +namespace mongo { +namespace rpc { + +namespace { + +const char kClusterTimeFieldName[] = "clusterTime"; +const char kSignatureFieldName[] = "signature"; + +} // unnamed namespace + + +LogicalTimeMetadata::LogicalTimeMetadata(SignedLogicalTime time) : _clusterTime(std::move(time)) {} +StatusWith<LogicalTimeMetadata> LogicalTimeMetadata::readFromMetadata(const BSONObj& metadata) { + return readFromMetadata(metadata.getField(fieldName())); +} + +StatusWith<LogicalTimeMetadata> LogicalTimeMetadata::readFromMetadata( + const BSONElement& metadataElem) { + const auto& obj = metadataElem.Obj(); + + Timestamp ts; + Status status = bsonExtractTimestampField(obj, kClusterTimeFieldName, &ts); + if (!status.isOK()) { + return status; + } + + // Extract BinData type signature and construct a SHA1Block instance from it. + BSONElement signatureElem; + status = bsonExtractTypedField(obj, kSignatureFieldName, BinData, &signatureElem); + if (!status.isOK()) { + return status; + } + + int hashLength = 0; + auto rawBinSignature = signatureElem.binData(hashLength); + BSONBinData proofBinData(rawBinSignature, hashLength, signatureElem.binDataType()); + auto proofStatus = SHA1Block::fromBinData(proofBinData); + + if (!proofStatus.isOK()) { + return proofStatus.getStatus(); + } + + return LogicalTimeMetadata( + SignedLogicalTime(LogicalTime(ts), std::move(proofStatus.getValue()))); +} + +void LogicalTimeMetadata::writeToMetadata(BSONObjBuilder* metadataBuilder) const { + BSONObjBuilder subObjBuilder(metadataBuilder->subobjStart(fieldName())); + _clusterTime.getTime().asTimestamp().append(subObjBuilder.bb(), kClusterTimeFieldName); + _clusterTime.getProof().appendAsBinData(subObjBuilder, kSignatureFieldName); + subObjBuilder.doneFast(); +} + +const SignedLogicalTime& LogicalTimeMetadata::getSignedTime() const { + return _clusterTime; +} + +} // namespace rpc +} // namespace mongo diff --git a/src/mongo/rpc/metadata/logical_time_metadata.h b/src/mongo/rpc/metadata/logical_time_metadata.h new file mode 100644 index 00000000000..aafd4640231 --- /dev/null +++ b/src/mongo/rpc/metadata/logical_time_metadata.h @@ -0,0 +1,68 @@ +/** + * 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 "mongo/base/status_with.h" +#include "mongo/db/signed_logical_time.h" + +namespace mongo { + +class BSONElement; +class BSONObjBuilder; + +namespace rpc { + +/** + * Format: + * logicalTime: { + * clusterTime: <Timestamp>, + * signature: <SHA1 hash of clusterTime as BinData> + * } + */ +class LogicalTimeMetadata { +public: + explicit LogicalTimeMetadata(SignedLogicalTime time); + + static StatusWith<LogicalTimeMetadata> readFromMetadata(const BSONObj& metadata); + static StatusWith<LogicalTimeMetadata> readFromMetadata(const BSONElement& metadataElem); + + void writeToMetadata(BSONObjBuilder* metadataBuilder) const; + + const SignedLogicalTime& getSignedTime() const; + + static StringData fieldName() { + return "logicalTime"; + } + +private: + SignedLogicalTime _clusterTime; +}; + +} // namespace rpc +} // namespace mongo diff --git a/src/mongo/rpc/metadata/logical_time_metadata_test.cpp b/src/mongo/rpc/metadata/logical_time_metadata_test.cpp new file mode 100644 index 00000000000..61106f3d469 --- /dev/null +++ b/src/mongo/rpc/metadata/logical_time_metadata_test.cpp @@ -0,0 +1,113 @@ +/** + * 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. + */ + +#define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kCommand + +#include "mongo/platform/basic.h" + +#include "mongo/db/jsobj.h" +#include "mongo/rpc/metadata/logical_time_metadata.h" +#include "mongo/unittest/unittest.h" + +#include "mongo/util/log.h" + +namespace mongo { +namespace rpc { +namespace { + +TEST(LogicalTimeMetadataTest, Roundtrip) { + const auto ts = LogicalTime(Timestamp(100, 200)); + + SHA1Block::HashType proof; + proof.fill(0); + proof[19] = 6; + proof[0] = 12; + + SignedLogicalTime signedTs(LogicalTime(ts), proof); + + LogicalTimeMetadata origMetadata(signedTs); + BSONObjBuilder builder; + origMetadata.writeToMetadata(&builder); + + auto serializedObj = builder.done(); + auto parseStatus = LogicalTimeMetadata::readFromMetadata(serializedObj); + ASSERT_OK(parseStatus.getStatus()); + + const auto& parsedMetadata = parseStatus.getValue(); + const auto& parsedTs = parsedMetadata.getSignedTime(); + ASSERT_EQ(ts.asTimestamp(), parsedTs.getTime().asTimestamp()); + ASSERT_TRUE(SHA1Block(proof) == parsedTs.getProof()); +} + +TEST(LogicalTimeMetadataTest, MissingClusterTimeShouldFailToParse) { + std::array<uint8_t, 20> proof; + proof.fill(0); + + BSONObjBuilder builder; + BSONObjBuilder subObjBuilder(builder.subobjStart("logicalTime")); + subObjBuilder.append("signature", BSONBinData(proof.data(), proof.size(), BinDataGeneral)); + subObjBuilder.doneFast(); + + auto serializedObj = builder.done(); + auto status = LogicalTimeMetadata::readFromMetadata(serializedObj).getStatus(); + ASSERT_EQ(ErrorCodes::NoSuchKey, status); +} + +TEST(LogicalTimeMetadataTest, MissingProofShouldFailToParse) { + const auto ts = Timestamp(100, 200); + + BSONObjBuilder builder; + BSONObjBuilder subObjBuilder(builder.subobjStart("logicalTime")); + ts.append(subObjBuilder.bb(), "clusterTime"); + subObjBuilder.doneFast(); + + auto serializedObj = builder.done(); + auto status = LogicalTimeMetadata::readFromMetadata(serializedObj).getStatus(); + ASSERT_EQ(ErrorCodes::NoSuchKey, status); +} + +TEST(LogicalTimeMetadataTest, ProofWithWrongLengthShouldFailToParse) { + const auto ts = Timestamp(100, 200); + + std::array<uint8_t, 10> proof; + proof.fill(0); + + BSONObjBuilder builder; + BSONObjBuilder subObjBuilder(builder.subobjStart("logicalTime")); + ts.append(subObjBuilder.bb(), "clusterTime"); + subObjBuilder.append("signature", BSONBinData(proof.data(), proof.size(), BinDataGeneral)); + subObjBuilder.doneFast(); + + auto serializedObj = builder.done(); + auto status = LogicalTimeMetadata::readFromMetadata(serializedObj).getStatus(); + ASSERT_EQ(ErrorCodes::UnsupportedFormat, status); +} + +} // namespace rpc +} // namespace mongo +} |