diff options
author | Ted Tuckman <ted.tuckman@mongodb.com> | 2019-01-31 09:03:34 -0500 |
---|---|---|
committer | Ted Tuckman <ted.tuckman@mongodb.com> | 2019-02-15 14:53:03 -0500 |
commit | da8140d860635544646e43e93a64ffb0fdd42a3e (patch) | |
tree | e780521729669cfec5828b20c02f4cfd0f41c27a /src/mongo/db/matcher | |
parent | 2fabe02b172a069cf3309683ea58ddab96c5c8a3 (diff) | |
download | mongo-da8140d860635544646e43e93a64ffb0fdd42a3e.tar.gz |
SERVER-39236 Add encrypt schema IDL types
Diffstat (limited to 'src/mongo/db/matcher')
-rw-r--r-- | src/mongo/db/matcher/SConscript | 15 | ||||
-rw-r--r-- | src/mongo/db/matcher/schema/encrypt_schema.idl | 132 | ||||
-rw-r--r-- | src/mongo/db/matcher/schema/encrypt_schema_types.cpp | 75 | ||||
-rw-r--r-- | src/mongo/db/matcher/schema/encrypt_schema_types.h | 121 | ||||
-rw-r--r-- | src/mongo/db/matcher/schema/encrypt_schema_types_test.cpp | 120 |
5 files changed, 461 insertions, 2 deletions
diff --git a/src/mongo/db/matcher/SConscript b/src/mongo/db/matcher/SConscript index b342cd71214..06d5c7b0549 100644 --- a/src/mongo/db/matcher/SConscript +++ b/src/mongo/db/matcher/SConscript @@ -51,6 +51,7 @@ env.Library( 'matcher.cpp', 'matcher_type_set.cpp', 'rewrite_expr.cpp', + 'schema/encrypt_schema_types.cpp', 'schema/expression_internal_schema_all_elem_match_from_index.cpp', 'schema/expression_internal_schema_allowed_properties.cpp', 'schema/expression_internal_schema_cond.cpp', @@ -66,6 +67,7 @@ env.Library( 'schema/expression_internal_schema_xor.cpp', 'schema/json_pointer.cpp', 'schema/json_schema_parser.cpp', + env.Idlc('schema/encrypt_schema.idl')[0], ], LIBDEPS=[ '$BUILD_DIR/mongo/base', @@ -77,12 +79,23 @@ env.Library( '$BUILD_DIR/mongo/db/query/collation/collator_interface', '$BUILD_DIR/mongo/db/query/query_knobs', '$BUILD_DIR/mongo/db/pipeline/expression', + '$BUILD_DIR/mongo/idl/idl_parser', '$BUILD_DIR/third_party/shim_pcrecpp', 'path', ], ) env.CppUnitTest( + target='encrypt_schema_types_test', + source=[ + 'schema/encrypt_schema_types_test.cpp' + ], + LIBDEPS=[ + 'expressions', + ], +) + +env.CppUnitTest( target='json_pointer_test', source=['schema/json_pointer_test.cpp'], LIBDEPS=[ @@ -190,5 +203,3 @@ env.CppUnitTest( 'expressions', ], ) - - diff --git a/src/mongo/db/matcher/schema/encrypt_schema.idl b/src/mongo/db/matcher/schema/encrypt_schema.idl new file mode 100644 index 00000000000..83112526f55 --- /dev/null +++ b/src/mongo/db/matcher/schema/encrypt_schema.idl @@ -0,0 +1,132 @@ +# Copyright (C) 2019-present MongoDB, Inc. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the Server Side Public License, version 1, +# as published by MongoDB, Inc. +# +# 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 +# Server Side Public License for more details. +# +# You should have received a copy of the Server Side Public License +# along with this program. If not, see +# <http://www.mongodb.com/licensing/server-side-public-license>. +# +# 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 Server Side 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. +# + +global: + cpp_namespace: "mongo" + cpp_includes: + - "mongo/db/matcher/schema/encrypt_schema_types.h" + +imports: + - "mongo/idl/basic_types.idl" + +types: + encryptSchemaAnyType: + bson_serialization_type: any + description: "Holds a to-be-encrypted BSONElement of any type." + cpp_type: "mongo::EncryptSchemaAnyType" + serializer: mongo::EncryptSchemaAnyType::serializeToBSON + deserializer: mongo::EncryptSchemaAnyType::parseFromBSON + + encryptSchemaKeyId: + bson_serialization_type: any + description: "A string pointing to the key id or an array UUIDs identifying a set of keys." + cpp_type: "mongo::EncryptSchemaKeyId" + serializer: mongo::EncryptSchemaKeyId::serializeToBSON + deserializer: mongo::EncryptSchemaKeyId::parseFromBSON + +enums: + FleAlgorithm: + description: "The algorithm used to encrypt fields for field level encryption." + type: string + values: + kDeterministic: "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + kRandom: "AEAD_AES_256_CBC_HMAC_SHA_512-Random" + +structs: + # Maps to the encrypt keyword in JSON Schema. + EncryptionInfo: + description: "Represents the fields that users can specify within the encryptMetadata + subobject in a JSON Schema." + strict: true + fields: + bsonType: + description: "Specifies the type of the to-be-encrypted value." + type: string + optional: true + algorithm: + description: "The encryption algorithm to be used." + type: FleAlgorithm + optional: true + initializationVector: + description: "Provided to be used for unique encryption for different collections." + type: bindata_generic + optional: true + keyId: + description: "A JSONPointer to a key or an array of UUIDs identifying a set of keys." + type: encryptSchemaKeyId + optional: true + + # Maps to "encryptMetadata" in JSON Schema. + EncryptionMetadata: + description: "The fields that can be applied to children in the schema + with the encrypt keyword." + strict: true + fields: + algorithm: + description: "The encryption algorithm to be used." + type: FleAlgorithm + optional: true + initializationVector: + description: "Provided to be used for unique encryption for different collections." + type: bindata_generic + optional: true + keyId: + description: "A JSONPointer to a key or an array of UUIDs identifying a set of keys." + type: encryptSchemaKeyId + optional: true + + EncryptionPlaceholder: + description: "Implements Encryption BinData (subtype 6) sub-subtype 0, the intent-to-encrypt + mapping. Contains a value to encrypt and a description of how it should be encrypted." + strict: true + fields: + a: + description: "The encryption algorithm to be used." + type: FleAlgorithm + cpp_name: algorithm + iv: + description: "Provided to be used for unique encryption for different collections." + type: bindata_generic + cpp_name: initializationVector + # Only present if algorithm is deterministic. + optional: true + ki: + description: "Used to query the key vault by _id. If omitted, ka must be specified." + type: uuid + cpp_name: keyId + optional: true + ka: + description: "Used to query the key vault by keyAltName. If omitted, + ki must be specified." + type: encryptSchemaAnyType + cpp_name: keyAltName + optional: true + v: + description: "value to encrypt" + type: encryptSchemaAnyType + cpp_name: encryptSchemaAnyType diff --git a/src/mongo/db/matcher/schema/encrypt_schema_types.cpp b/src/mongo/db/matcher/schema/encrypt_schema_types.cpp new file mode 100644 index 00000000000..5ce2fbfabaa --- /dev/null +++ b/src/mongo/db/matcher/schema/encrypt_schema_types.cpp @@ -0,0 +1,75 @@ +/** + * Copyright (C) 2019-present MongoDB, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the Server Side Public License, version 1, + * as published by MongoDB, Inc. + * + * 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 + * Server Side Public License for more details. + * + * You should have received a copy of the Server Side Public License + * along with this program. If not, see + * <http://www.mongodb.com/licensing/server-side-public-license>. + * + * 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 Server Side 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/matcher/schema/encrypt_schema_types.h" +#include "mongo/idl/idl_parser.h" + +namespace mongo { + +EncryptSchemaKeyId EncryptSchemaKeyId::parseFromBSON(const BSONElement& element) { + if (element.type() == BSONType::String) { + return EncryptSchemaKeyId(element.String()); + } else if (element.type() == BSONType::Array) { + std::vector<UUID> keys; + + for (auto&& arrayElement : element.embeddedObject()) { + if (arrayElement.binDataType() == BinDataType::newUUID) { + const auto uuid = uassertStatusOK(UUID::parse(arrayElement)); + + keys.emplace_back(uuid); + } else { + uasserted(51084, + str::stream() << "Array elements must have type UUID, found " + << arrayElement.binDataType()); + } + } + return EncryptSchemaKeyId(keys); + } else { + uasserted(51085, + str::stream() + << "Expected either string or array of UUID for EncryptSchemaKeyId, found " + << element.type()); + } + MONGO_UNREACHABLE; +} + +void EncryptSchemaKeyId::serializeToBSON(StringData fieldName, BSONObjBuilder* builder) const { + if (_type == Type::kUUIDs) { + BSONArrayBuilder arrBuilder(builder->subarrayStart(fieldName)); + for (auto uuid : _uuids) { + uuid.appendToArrayBuilder(&arrBuilder); + } + arrBuilder.doneFast(); + } else { + builder->append(fieldName, _strKeyId); + } +} +} // namespace mongo diff --git a/src/mongo/db/matcher/schema/encrypt_schema_types.h b/src/mongo/db/matcher/schema/encrypt_schema_types.h new file mode 100644 index 00000000000..504b605cca5 --- /dev/null +++ b/src/mongo/db/matcher/schema/encrypt_schema_types.h @@ -0,0 +1,121 @@ +/** + * Copyright (C) 2019-present MongoDB, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the Server Side Public License, version 1, + * as published by MongoDB, Inc. + * + * 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 + * Server Side Public License for more details. + * + * You should have received a copy of the Server Side Public License + * along with this program. If not, see + * <http://www.mongodb.com/licensing/server-side-public-license>. + * + * 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 Server Side 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 <vector> + +#include "mongo/bson/bsonelement.h" +#include "mongo/bson/bsonobj.h" +#include "mongo/bson/bsonobjbuilder.h" +#include "mongo/util/uuid.h" + +namespace mongo { + +/** + * A string pointing to the key id or an array of UUIDs identifying a set of keys. + */ +class EncryptSchemaKeyId { + friend class EncryptionInfoNormalized; + friend class EncryptionMetadata; + friend class EncryptionInfo; + +public: + enum class Type { + kUUIDs, + kJSONPointer, + }; + + static EncryptSchemaKeyId parseFromBSON(const BSONElement& element); + + EncryptSchemaKeyId(const std::string key) + : _strKeyId(std::move(key)), _type(Type::kJSONPointer) {} + + EncryptSchemaKeyId(std::vector<UUID> keys) : _uuids(std::move(keys)), _type(Type::kUUIDs) {} + + void serializeToBSON(StringData fieldName, BSONObjBuilder* builder) const; + + Type type() const { + return _type; + } + + /** + * Callers must check that the result of type() is kUUIDs first. + */ + const std::vector<UUID>& uuids() const { + invariant(_type == Type::kUUIDs); + return _uuids; + } + + /** + * Callers must check that the result of type() is kJSONPointer first. + */ + const std::string& jsonPointer() const { + invariant(_type == Type::kJSONPointer); + return _strKeyId; + } + +private: + // The default constructor is required to exist by IDL, but is private because it does not + // construct a valid EncryptSchemaKeyId and should not be called. + EncryptSchemaKeyId() = default; + + std::string _strKeyId; + std::vector<UUID> _uuids; + + Type _type; +}; + +/** + * Class to represent an element with any type from IDL. The caller must ensure that the backing + * BSON stays alive while this type is in use. + */ +class EncryptSchemaAnyType { +public: + /** + * This type is currenty only used for serialization, not parsing. + */ + static EncryptSchemaAnyType parseFromBSON(const BSONElement& element) { + MONGO_UNREACHABLE; + } + + void serializeToBSON(StringData fieldName, BSONObjBuilder* builder) const { + builder->appendAs(_element, fieldName); + } + + void setElement(const BSONElement& element) { + _element = element; + } + +private: + BSONElement _element; +}; + +} // namespace mongo diff --git a/src/mongo/db/matcher/schema/encrypt_schema_types_test.cpp b/src/mongo/db/matcher/schema/encrypt_schema_types_test.cpp new file mode 100644 index 00000000000..229b66ccb0f --- /dev/null +++ b/src/mongo/db/matcher/schema/encrypt_schema_types_test.cpp @@ -0,0 +1,120 @@ +/** + * Copyright (C) 2019-present MongoDB, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the Server Side Public License, version 1, + * as published by MongoDB, Inc. + * + * 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 + * Server Side Public License for more details. + * + * You should have received a copy of the Server Side Public License + * along with this program. If not, see + * <http://www.mongodb.com/licensing/server-side-public-license>. + * + * 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 Server Side 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/bsonelement.h" +#include "mongo/bson/bsonobjbuilder.h" +#include "mongo/db/matcher/schema/encrypt_schema_gen.h" +#include "mongo/db/matcher/schema/encrypt_schema_types.h" +#include "mongo/unittest/bson_test_util.h" +#include "mongo/unittest/death_test.h" +#include "mongo/unittest/unittest.h" +#include "mongo/util/uuid.h" + +namespace mongo { +namespace { + +TEST(EncryptSchemaTest, KeyIDTypePointerTest) { + auto tempObj = BSON("pointer" + << "/pointer/pointing"); + auto elem = tempObj["pointer"]; + auto keyid = EncryptSchemaKeyId::parseFromBSON(elem); + ASSERT(keyid.type() == EncryptSchemaKeyId::Type::kJSONPointer); + ASSERT_FALSE(UUID::parse(keyid.jsonPointer()).isOK()); +} + +TEST(EncryptSchemaTest, KeyIDTypeArrayTest) { + auto tempObj = + BSON("array" << BSON_ARRAY(UUID::gen() << UUID::gen() << UUID::gen() << UUID::gen())); + auto elem = tempObj["array"]; + auto keyid = EncryptSchemaKeyId::parseFromBSON(elem); + ASSERT(keyid.type() == EncryptSchemaKeyId::Type::kUUIDs); +} + +TEST(EncryptSchemaTest, KeyIDTypeInvalidTest) { + auto tempObj = BSON("invalid" << 5); + auto elem = tempObj["invalid"]; + ASSERT_THROWS(EncryptSchemaKeyId::parseFromBSON(elem), DBException); +} + +DEATH_TEST(EncryptSchemaTest, KeyIDPointerToBSON, "invariant") { + BSONObjBuilder builder; + EncryptSchemaKeyId pointerKeyID{"/pointer"}; + pointerKeyID.serializeToBSON("pointer", &builder); + auto resultObj = builder.obj(); + BSONElement pointer = resultObj["pointer"]; + ASSERT(pointer); + ASSERT_EQ(pointer.type(), BSONType::String); + EncryptSchemaKeyId pointerParsed = EncryptSchemaKeyId::parseFromBSON(pointer); + pointerParsed.uuids(); +} + +DEATH_TEST(EncryptSchemaTest, KeyIDArrayToBSON, "invariant") { + BSONObjBuilder builder; + std::vector<UUID> vect{UUID::gen(), UUID::gen()}; + EncryptSchemaKeyId vectKeyId{vect}; + vectKeyId.serializeToBSON("array", &builder); + auto resultObj = builder.obj(); + BSONElement array = resultObj["array"]; + ASSERT(array); + ASSERT_EQ(array.type(), BSONType::Array); + ASSERT_EQ(array.Array().size(), unsigned{2}); + EncryptSchemaKeyId parsed = EncryptSchemaKeyId::parseFromBSON(array); + parsed.jsonPointer(); +} + +TEST(EncryptSchemaTest, ParseFullEncryptObjectFromBSON) { + BSONObj encryptInfoBSON = BSON("bsonType" + << "int" + << "algorithm" + << "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + << "keyId" + << "/pointer"); + IDLParserErrorContext ctxt("encrypt"); + auto encryptInfo = EncryptionInfo::parse(ctxt, encryptInfoBSON); + ASSERT_EQ(encryptInfo.getBsonType().get(), "int"); + ASSERT_TRUE(encryptInfo.getAlgorithm().get() == FleAlgorithmEnum::kDeterministic); + EncryptSchemaKeyId keyid = encryptInfo.getKeyId().get(); + ASSERT_TRUE(keyid.type() == EncryptSchemaKeyId::Type::kJSONPointer); + ASSERT_EQ(keyid.jsonPointer(), "/pointer"); +} + +TEST(EncryptSchemaTest, WrongTypeFailsParse) { + BSONObj encryptInfoBSON = BSON("keyId" << 2); + IDLParserErrorContext ctxt("encrypt"); + ASSERT_THROWS_CODE(EncryptionInfo::parse(ctxt, encryptInfoBSON), DBException, 51085); + encryptInfoBSON = BSON("algorithm" + << "garbage"); + ASSERT_THROWS_CODE( + EncryptionInfo::parse(ctxt, encryptInfoBSON), DBException, ErrorCodes::BadValue); +} + +} // namespace +} // namespace mongo |