Beispiel #1
0
async def main():
    # This must be the same master key that was used to create
    # the encryption key.
    local_master_key = os.urandom(96)
    kms_providers = {"local": {"key": local_master_key}}

    # The MongoDB namespace (db.collection) used to store
    # the encryption data keys.
    key_vault_namespace = "encryption.__pymongoTestKeyVault"
    key_vault_db_name, key_vault_coll_name = key_vault_namespace.split(".", 1)

    # bypass_auto_encryption=True disable automatic encryption but keeps
    # the automatic _decryption_ behavior. bypass_auto_encryption will
    # also disable spawning mongocryptd.
    auto_encryption_opts = AutoEncryptionOpts(
        kms_providers, key_vault_namespace, bypass_auto_encryption=True)

    client = AsyncIOMotorClient(auto_encryption_opts=auto_encryption_opts)
    coll = client.test.coll
    # Clear old data
    await coll.drop()

    # Set up the key vault (key_vault_namespace) for this example.
    key_vault = client[key_vault_db_name][key_vault_coll_name]
    # Ensure that two data keys cannot share the same keyAltName.
    await key_vault.drop()
    await key_vault.create_index(
        "keyAltNames",
        unique=True,
        partialFilterExpression={"keyAltNames": {"$exists": True}})

    client_encryption = AsyncIOMotorClientEncryption(
        kms_providers,
        key_vault_namespace,
        # The MotorClient to use for reading/writing to the key vault.
        # This can be the same MotorClient used by the main application.
        client,
        # The CodecOptions class used for encrypting and decrypting.
        # This should be the same CodecOptions instance you have configured
        # on MotorClient, Database, or Collection.
        coll.codec_options)

    # Create a new data key for the encryptedField.
    data_key_id = await client_encryption.create_data_key(
        'local', key_alt_names=['pymongo_encryption_example_4'])

    # Explicitly encrypt a field:
    encrypted_field = await client_encryption.encrypt(
        "123456789",
        Algorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Deterministic,
        key_alt_name='pymongo_encryption_example_4')
    await coll.insert_one({"encryptedField": encrypted_field})
    # Automatically decrypts any encrypted fields.
    doc = await coll.find_one()
    print('Decrypted document: %s' % (doc,))
    unencrypted_coll = AsyncIOMotorClient().test.coll
    print('Encrypted document: %s' % (await unencrypted_coll.find_one(),))

    # Cleanup resources.
    await client_encryption.close()
Beispiel #2
0
    def setUpClass(cls):
        super(TestBsonSizeBatches, cls).setUpClass()
        db = client_context.client.db
        cls.coll = db.coll
        cls.coll.drop()
        # Configure the encrypted 'db.coll' collection via jsonSchema.
        json_schema = json_data('limits', 'limits-schema.json')
        db.create_collection('coll',
                             validator={'$jsonSchema': json_schema},
                             codec_options=OPTS,
                             write_concern=WriteConcern(w='majority'))

        # Create the key vault.
        coll = client_context.client.get_database(
            'keyvault',
            write_concern=WriteConcern(w='majority'),
            codec_options=OPTS)['datakeys']
        coll.drop()
        coll.insert_one(json_data('limits', 'limits-key.json'))

        opts = AutoEncryptionOpts({'local': {
            'key': LOCAL_MASTER_KEY
        }}, 'keyvault.datakeys')
        cls.listener = OvertCommandListener()
        cls.client_encrypted = rs_or_single_client(
            auto_encryption_opts=opts, event_listeners=[cls.listener])
        cls.coll_encrypted = cls.client_encrypted.db.coll
Beispiel #3
0
    def test_auto_encrypt_local_schema_map(self):
        # Configure the encrypted field via the local schema_map option.
        schemas = {'pymongo_test.test': json_data('custom', 'schema.json')}
        opts = AutoEncryptionOpts(
            KMS_PROVIDERS, 'admin.datakeys', schema_map=schemas)

        self._test_auto_encrypt(opts)
Beispiel #4
0
 def test_corpus_local_schema(self):
     # Configure the encrypted field via the local schema_map option.
     schemas = {'db.coll': self.fix_up_schema(
         json_data('corpus', 'corpus-schema.json'))}
     opts = AutoEncryptionOpts(
         self.kms_providers(), 'admin.datakeys', schema_map=schemas)
     self._test_corpus(opts)
Beispiel #5
0
    def test_auto_encrypt(self):
        # Configure the encrypted field via jsonSchema.
        json_schema = json_data('custom', 'schema.json')
        create_with_schema(self.db.test, json_schema)
        self.addCleanup(self.db.test.drop)

        opts = AutoEncryptionOpts(KMS_PROVIDERS, 'keyvault.datakeys')
        self._test_auto_encrypt(opts)
Beispiel #6
0
 def get_csfle_enabled_client(self, schema):
     return MongoClient(
         self.connection_string,
         auto_encryption_opts=AutoEncryptionOpts(
             self.kms_provider,
             self.key_vault_namespace,
             mongocryptd_bypass_spawn=self.mongocryptd_bypass_spawn,
             mongocryptd_spawn_path=self.mongocryptd_spawn_path,
             schema_map=schema))
Beispiel #7
0
    def test_auto_encrypt(self):
        # Configure the encrypted field via jsonSchema.
        json_schema = json_data('custom', 'schema.json')
        coll = self.db.create_collection(
            'test', validator={'$jsonSchema': json_schema}, codec_options=OPTS)
        self.addCleanup(coll.drop)

        opts = AutoEncryptionOpts(KMS_PROVIDERS, 'admin.datakeys')
        self._test_auto_encrypt(opts)
Beispiel #8
0
    def __init__(self, kms, schema):
        key_vault_namespace = "encryption.__keyVault"

        fle_opts = AutoEncryptionOpts(kms,
                                      key_vault_namespace,
                                      schema_map=schema,
                                      **{})
        self.client = MongoClient(mongo_url, auto_encryption_opts=fle_opts)
        self.collection = self.client[db][collection]
        print(f'encrypted connection initiated. \n')
Beispiel #9
0
    def test_use_after_close(self):
        opts = AutoEncryptionOpts(KMS_PROVIDERS, 'keyvault.datakeys')
        client = rs_or_single_client(auto_encryption_opts=opts)
        self.addCleanup(client.close)

        client.admin.command('isMaster')
        client.close()
        with self.assertRaisesRegex(InvalidOperation,
                                    'Cannot use MongoClient after close'):
            client.admin.command('isMaster')
Beispiel #10
0
    def _test_external_key_vault(self, with_external_key_vault):
        self.client.db.coll.drop()
        vault = create_key_vault(self.client.keyvault.datakeys,
                                 json_data('corpus', 'corpus-key-local.json'),
                                 json_data('corpus', 'corpus-key-aws.json'))
        self.addCleanup(vault.drop)

        # Configure the encrypted field via the local schema_map option.
        schemas = {'db.coll': json_data('external', 'external-schema.json')}
        if with_external_key_vault:
            key_vault_client = rs_or_single_client(username='******',
                                                   password='******')
            self.addCleanup(key_vault_client.close)
        else:
            key_vault_client = client_context.client
        opts = AutoEncryptionOpts(self.kms_providers(),
                                  'keyvault.datakeys',
                                  schema_map=schemas,
                                  key_vault_client=key_vault_client)

        client_encrypted = rs_or_single_client(auto_encryption_opts=opts,
                                               uuidRepresentation='standard')
        self.addCleanup(client_encrypted.close)

        client_encryption = ClientEncryption(self.kms_providers(),
                                             'keyvault.datakeys',
                                             key_vault_client, OPTS)
        self.addCleanup(client_encryption.close)

        if with_external_key_vault:
            # Authentication error.
            with self.assertRaises(EncryptionError) as ctx:
                client_encrypted.db.coll.insert_one({"encrypted": "test"})
            # AuthenticationFailed error.
            self.assertIsInstance(ctx.exception.cause, OperationFailure)
            self.assertEqual(ctx.exception.cause.code, 18)
        else:
            client_encrypted.db.coll.insert_one({"encrypted": "test"})

        if with_external_key_vault:
            # Authentication error.
            with self.assertRaises(EncryptionError) as ctx:
                client_encryption.encrypt(
                    "test",
                    Algorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Deterministic,
                    key_id=LOCAL_KEY_ID)
            # AuthenticationFailed error.
            self.assertIsInstance(ctx.exception.cause, OperationFailure)
            self.assertEqual(ctx.exception.cause.code, 18)
        else:
            client_encryption.encrypt(
                "test",
                Algorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Deterministic,
                key_id=LOCAL_KEY_ID)
Beispiel #11
0
 def parse_auto_encrypt_opts(self, opts):
     """Parse clientOptions.autoEncryptOpts."""
     opts = camel_to_snake_args(opts)
     kms_providers = opts['kms_providers']
     if 'aws' in kms_providers:
         kms_providers['aws'] = AWS_CREDS
         if not any(AWS_CREDS.values()):
             self.skipTest('AWS environment credentials are not set')
     if 'key_vault_namespace' not in opts:
         opts['key_vault_namespace'] = 'keyvault.datakeys'
     opts = dict(opts)
     return AutoEncryptionOpts(**opts)
Beispiel #12
0
    def test_init_spawn_args(self):
        # User can override idleShutdownTimeoutSecs
        opts = AutoEncryptionOpts(
            {}, 'admin.datakeys',
            mongocryptd_spawn_args=['--idleShutdownTimeoutSecs=88'])
        self.assertEqual(
            opts._mongocryptd_spawn_args, ['--idleShutdownTimeoutSecs=88'])

        # idleShutdownTimeoutSecs is added by default
        opts = AutoEncryptionOpts(
            {}, 'admin.datakeys', mongocryptd_spawn_args=[])
        self.assertEqual(
            opts._mongocryptd_spawn_args, ['--idleShutdownTimeoutSecs=60'])

        # Also added when other options are given
        opts = AutoEncryptionOpts(
            {}, 'admin.datakeys',
            mongocryptd_spawn_args=['--quiet', '--port=27020'])
        self.assertEqual(
            opts._mongocryptd_spawn_args,
            ['--quiet', '--port=27020', '--idleShutdownTimeoutSecs=60'])
Beispiel #13
0
 def test_init(self):
     opts = AutoEncryptionOpts({}, 'keyvault.datakeys')
     self.assertEqual(opts._kms_providers, {})
     self.assertEqual(opts._key_vault_namespace, 'keyvault.datakeys')
     self.assertEqual(opts._key_vault_client, None)
     self.assertEqual(opts._schema_map, None)
     self.assertEqual(opts._bypass_auto_encryption, False)
     self.assertEqual(opts._mongocryptd_uri, 'mongodb://localhost:27020')
     self.assertEqual(opts._mongocryptd_bypass_spawn, False)
     self.assertEqual(opts._mongocryptd_spawn_path, 'mongocryptd')
     self.assertEqual(opts._mongocryptd_spawn_args,
                      ['--idleShutdownTimeoutSecs=60'])
async def main():
    # The MongoDB namespace (db.collection) used to store the
    # encrypted documents in this example.
    encrypted_namespace = "test.coll"

    # This must be the same master key that was used to create
    # the encryption key.
    local_master_key = os.urandom(96)
    kms_providers = {"local": {"key": local_master_key}}

    # The MongoDB namespace (db.collection) used to store
    # the encryption data keys.
    key_vault_namespace = "encryption.__pymongoTestKeyVault"
    key_vault_db_name, key_vault_coll_name = key_vault_namespace.split(".", 1)

    # The MotorClient used to access the key vault (key_vault_namespace).
    key_vault_client = AsyncIOMotorClient()
    key_vault = key_vault_client[key_vault_db_name][key_vault_coll_name]
    # Ensure that two data keys cannot share the same keyAltName.
    await key_vault.drop()
    await key_vault.create_index(
        "keyAltNames",
        unique=True,
        partialFilterExpression={"keyAltNames": {
            "$exists": True
        }})

    await create_json_schema_file(kms_providers, key_vault_namespace,
                                  key_vault_client)

    # Load the JSON Schema and construct the local schema_map option.
    with open('jsonSchema.json', 'r') as file:
        json_schema_string = file.read()
    json_schema = json_util.loads(json_schema_string)
    schema_map = {encrypted_namespace: json_schema}

    auto_encryption_opts = AutoEncryptionOpts(kms_providers,
                                              key_vault_namespace,
                                              schema_map=schema_map)

    client = AsyncIOMotorClient(auto_encryption_opts=auto_encryption_opts)
    db_name, coll_name = encrypted_namespace.split(".", 1)
    coll = client[db_name][coll_name]
    # Clear old data
    await coll.drop()

    await coll.insert_one({"encryptedField": "123456789"})
    decrypted_doc = await coll.find_one()
    print('Decrypted document: %s' % (decrypted_doc, ))
    unencrypted_coll = AsyncIOMotorClient()[db_name][coll_name]
    encrypted_doc = await unencrypted_coll.find_one()
    print('Encrypted document: %s' % (encrypted_doc, ))
Beispiel #15
0
 def test_raise_max_wire_version_error(self):
     opts = AutoEncryptionOpts(KMS_PROVIDERS, 'keyvault.datakeys')
     client = rs_or_single_client(auto_encryption_opts=opts)
     self.addCleanup(client.close)
     msg = 'Auto-encryption requires a minimum MongoDB version of 4.2'
     with self.assertRaisesRegex(ConfigurationError, msg):
         client.test.test.insert_one({})
     with self.assertRaisesRegex(ConfigurationError, msg):
         client.admin.command('isMaster')
     with self.assertRaisesRegex(ConfigurationError, msg):
         client.test.test.find_one({})
     with self.assertRaisesRegex(ConfigurationError, msg):
         client.test.test.bulk_write([InsertOne({})])
Beispiel #16
0
    def test_views_are_prohibited(self):
        self.client.db.view.drop()
        self.client.db.create_collection('view', viewOn='coll')
        self.addCleanup(self.client.db.view.drop)

        opts = AutoEncryptionOpts(self.kms_providers(), 'keyvault.datakeys')
        client_encrypted = rs_or_single_client(auto_encryption_opts=opts,
                                               uuidRepresentation='standard')
        self.addCleanup(client_encrypted.close)

        with self.assertRaisesRegex(EncryptionError,
                                    'cannot auto encrypt a view'):
            client_encrypted.db.view.insert_one({})
Beispiel #17
0
    def test_raise_unsupported_error(self):
        opts = AutoEncryptionOpts(KMS_PROVIDERS, 'keyvault.datakeys')
        client = rs_or_single_client(auto_encryption_opts=opts)
        self.addCleanup(client.close)
        msg = 'find_raw_batches does not support auto encryption'
        with self.assertRaisesRegex(InvalidOperation, msg):
            client.test.test.find_raw_batches({})

        msg = 'aggregate_raw_batches does not support auto encryption'
        with self.assertRaisesRegex(InvalidOperation, msg):
            client.test.test.aggregate_raw_batches([])

        if client_context.is_mongos:
            msg = 'Exhaust cursors are not supported by mongos'
        else:
            msg = 'exhaust cursors do not support auto encryption'
        with self.assertRaisesRegex(InvalidOperation, msg):
            next(client.test.test.find(cursor_type=CursorType.EXHAUST))
    def test_init(self):
        opts = AutoEncryptionOpts({}, 'admin.datakeys')
        self.assertEqual(opts._kms_providers, {})
        self.assertEqual(opts._key_vault_namespace, 'admin.datakeys')
        self.assertEqual(opts._key_vault_client, None)
        self.assertEqual(opts._schema_map, None)
        self.assertEqual(opts._bypass_auto_encryption, False)

        if hasattr(socket, 'AF_UNIX'):
            self.assertEqual(opts._mongocryptd_uri,
                             'mongodb://%2Ftmp%2Fmongocryptd.sock')
        else:
            self.assertEqual(opts._mongocryptd_uri,
                             'mongodb://localhost:27020')

        self.assertEqual(opts._mongocryptd_bypass_spawn, False)
        self.assertEqual(opts._mongocryptd_spawn_path, 'mongocryptd')
        self.assertEqual(opts._mongocryptd_spawn_args,
                         ['--idleShutdownTimeoutSecs=60'])
Beispiel #19
0
    "lab7.data": {
        "bsonType": "object",
        "properties": {
            "ssn": {
                "encrypt": {
                    "bsonType": "string",
                    "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic",
                    "keyId": [key_uuid]
                }
            },
        }
    }
}

encryption_opts = AutoEncryptionOpts(kms_providers,
                                     "lab7.key_vault",
                                     schema_map=schema_map,
                                     mongocryptd_bypass_spawn=True)

encrypted_data = MongoClient(auto_encryption_opts=encryption_opts).lab7.data
unencrypted_data = MongoClient().lab7.data

encrypted_data.insert_one({
    "name": "Doris",
    "surname": "Jones",
    "ssn": "457-55-5462"
})
encrypted_data.insert_one({
    "name": "Ellis",
    "surname": "Smith",
    "ssn": "234-45-1111"
})
    def __init__(self, kms_providers, key_vault_namespace, key_vault_client,
                 codec_options, kms_tls_options=None):
        """Explicit client-side field level encryption.

        The ClientEncryption class encapsulates explicit operations on a key
        vault collection that cannot be done directly on a MongoClient. Similar
        to configuring auto encryption on a MongoClient, it is constructed with
        a MongoClient (to a MongoDB cluster containing the key vault
        collection), KMS provider configuration, and keyVaultNamespace. It
        provides an API for explicitly encrypting and decrypting values, and
        creating data keys. It does not provide an API to query keys from the
        key vault collection, as this can be done directly on the MongoClient.

        See :ref:`explicit-client-side-encryption` for an example.

        :Parameters:
          - `kms_providers`: Map of KMS provider options. The `kms_providers`
            map values differ by provider:

              - `aws`: Map with "accessKeyId" and "secretAccessKey" as strings.
                These are the AWS access key ID and AWS secret access key used
                to generate KMS messages. An optional "sessionToken" may be
                included to support temporary AWS credentials.
              - `azure`: Map with "tenantId", "clientId", and "clientSecret" as
                strings. Additionally, "identityPlatformEndpoint" may also be
                specified as a string (defaults to 'login.microsoftonline.com').
                These are the Azure Active Directory credentials used to
                generate Azure Key Vault messages.
              - `gcp`: Map with "email" as a string and "privateKey"
                as `bytes` or a base64 encoded string.
                Additionally, "endpoint" may also be specified as a string
                (defaults to 'oauth2.googleapis.com'). These are the
                credentials used to generate Google Cloud KMS messages.
              - `kmip`: Map with "endpoint" as a host with required port.
                For example: ``{"endpoint": "example.com:443"}``.
              - `local`: Map with "key" as `bytes` (96 bytes in length) or
                a base64 encoded string which decodes
                to 96 bytes. "key" is the master key used to encrypt/decrypt
                data keys. This key should be generated and stored as securely
                as possible.

          - `key_vault_namespace`: The namespace for the key vault collection.
            The key vault collection contains all data keys used for encryption
            and decryption. Data keys are stored as documents in this MongoDB
            collection. Data keys are protected with encryption by a KMS
            provider.
          - `key_vault_client`: A MongoClient connected to a MongoDB cluster
            containing the `key_vault_namespace` collection.
          - `codec_options`: An instance of
            :class:`~bson.codec_options.CodecOptions` to use when encoding a
            value for encryption and decoding the decrypted BSON value. This
            should be the same CodecOptions instance configured on the
            MongoClient, Database, or Collection used to access application
            data.
          - `kms_tls_options` (optional): A map of KMS provider names to TLS
            options to use when creating secure connections to KMS providers.
            Accepts the same TLS options as
            :class:`pymongo.mongo_client.MongoClient`. For example, to
            override the system default CA file::

              kms_tls_options={'kmip': {'tlsCAFile': certifi.where()}}

            Or to supply a client certificate::

              kms_tls_options={'kmip': {'tlsCertificateKeyFile': 'client.pem'}}

        .. versionchanged:: 4.0
           Added the `kms_tls_options` parameter and the "kmip" KMS provider.

        .. versionadded:: 3.9
        """
        if not _HAVE_PYMONGOCRYPT:
            raise ConfigurationError(
                "client-side field level encryption requires the pymongocrypt "
                "library: install a compatible version with: "
                "python -m pip install 'pymongo[encryption]'")

        if not isinstance(codec_options, CodecOptions):
            raise TypeError("codec_options must be an instance of "
                            "bson.codec_options.CodecOptions")

        self._kms_providers = kms_providers
        self._key_vault_namespace = key_vault_namespace
        self._key_vault_client = key_vault_client
        self._codec_options = codec_options

        db, coll = key_vault_namespace.split('.', 1)
        key_vault_coll = key_vault_client[db][coll]

        opts = AutoEncryptionOpts(kms_providers, key_vault_namespace,
                                  kms_tls_options=kms_tls_options)
        self._io_callbacks = _EncryptionIO(None, key_vault_coll, None, opts)
        self._encryption = ExplicitEncrypter(
            self._io_callbacks, MongoCryptOptions(kms_providers, None))
async def main():
    # The MongoDB namespace (db.collection) used to store the
    # encrypted documents in this example.
    encrypted_namespace = "test.coll"

    # This must be the same master key that was used to create
    # the encryption key.
    local_master_key = os.urandom(96)
    kms_providers = {"local": {"key": local_master_key}}

    # The MongoDB namespace (db.collection) used to store
    # the encryption data keys.
    key_vault_namespace = "encryption.__pymongoTestKeyVault"
    key_vault_db_name, key_vault_coll_name = key_vault_namespace.split(".", 1)

    # The MotorClient used to access the key vault (key_vault_namespace).
    key_vault_client = AsyncIOMotorClient()
    key_vault = key_vault_client[key_vault_db_name][key_vault_coll_name]
    # Ensure that two data keys cannot share the same keyAltName.
    await key_vault.drop()
    await key_vault.create_index(
        "keyAltNames",
        unique=True,
        partialFilterExpression={"keyAltNames": {"$exists": True}})

    client_encryption = AsyncIOMotorClientEncryption(
        kms_providers,
        key_vault_namespace,
        key_vault_client,
        # The CodecOptions class used for encrypting and decrypting.
        # This should be the same CodecOptions instance you have configured
        # on MotorClient, Database, or Collection. We will not be calling
        # encrypt() or decrypt() in this example so we can use any
        # CodecOptions.
        CodecOptions())

    # Create a new data key and json schema for the encryptedField.
    data_key_id = await client_encryption.create_data_key(
        'local', key_alt_names=['pymongo_encryption_example_2'])
    json_schema = {
        "properties": {
            "encryptedField": {
                "encrypt": {
                    "keyId": [data_key_id],
                    "bsonType": "string",
                    "algorithm":
                        Algorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Deterministic
                }
            }
        },
        "bsonType": "object"
    }

    auto_encryption_opts = AutoEncryptionOpts(
        kms_providers, key_vault_namespace)
    client = AsyncIOMotorClient(auto_encryption_opts=auto_encryption_opts)
    db_name, coll_name = encrypted_namespace.split(".", 1)
    db = client[db_name]
    # Clear old data
    await db.drop_collection(coll_name)
    # Create the collection with the encryption JSON Schema.
    await db.create_collection(
        coll_name,
        # uuid_representation=STANDARD is required to ensure that any
        # UUIDs in the $jsonSchema document are encoded to BSON Binary
        # with the standard UUID subtype 4. This is only needed when
        # running the "create" collection command with an encryption
        # JSON Schema.
        codec_options=CodecOptions(uuid_representation=STANDARD),
        write_concern=WriteConcern(w="majority"),
        validator={"$jsonSchema": json_schema})
    coll = client[db_name][coll_name]

    await coll.insert_one({"encryptedField": "123456789"})
    decrypted_doc = await coll.find_one()
    print('Decrypted document: %s' % (decrypted_doc,))
    unencrypted_coll = AsyncIOMotorClient()[db_name][coll_name]
    encrypted_doc = await unencrypted_coll.find_one()
    print('Encrypted document: %s' % (encrypted_doc,))
    try:
        await unencrypted_coll.insert_one({"encryptedField": "123456789"})
    except OperationFailure as exc:
        print('Unencrypted insert failed: %s' % (exc.details,))
Beispiel #22
0
    def test_data_key(self):
        listener = OvertCommandListener()
        client = rs_or_single_client(event_listeners=[listener])
        self.addCleanup(client.close)
        client.db.coll.drop()
        vault = create_key_vault(client.keyvault.datakeys)
        self.addCleanup(vault.drop)

        # Configure the encrypted field via the local schema_map option.
        schemas = {
            "db.coll": {
                "bsonType": "object",
                "properties": {
                    "encrypted_placeholder": {
                        "encrypt": {
                            "keyId": "/placeholder",
                            "bsonType": "string",
                            "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random"
                        }
                    }
                }
            }
        }
        opts = AutoEncryptionOpts(self.kms_providers(),
                                  'keyvault.datakeys',
                                  schema_map=schemas)
        client_encrypted = rs_or_single_client(auto_encryption_opts=opts,
                                               uuidRepresentation='standard')
        self.addCleanup(client_encrypted.close)

        client_encryption = ClientEncryption(self.kms_providers(),
                                             'keyvault.datakeys', client, OPTS)
        self.addCleanup(client_encryption.close)

        # Local create data key.
        listener.reset()
        local_datakey_id = client_encryption.create_data_key(
            'local', key_alt_names=['local_altname'])
        self.assertBinaryUUID(local_datakey_id)
        cmd = listener.results['started'][-1]
        self.assertEqual('insert', cmd.command_name)
        self.assertEqual({'w': 'majority'}, cmd.command.get('writeConcern'))
        docs = list(vault.find({'_id': local_datakey_id}))
        self.assertEqual(len(docs), 1)
        self.assertEqual(docs[0]['masterKey']['provider'], 'local')

        # Local encrypt by key_id.
        local_encrypted = client_encryption.encrypt(
            'hello local',
            Algorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Deterministic,
            key_id=local_datakey_id)
        self.assertEncrypted(local_encrypted)
        client_encrypted.db.coll.insert_one({
            '_id': 'local',
            'value': local_encrypted
        })
        doc_decrypted = client_encrypted.db.coll.find_one({'_id': 'local'})
        self.assertEqual(doc_decrypted['value'], 'hello local')

        # Local encrypt by key_alt_name.
        local_encrypted_altname = client_encryption.encrypt(
            'hello local',
            Algorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Deterministic,
            key_alt_name='local_altname')
        self.assertEqual(local_encrypted_altname, local_encrypted)

        # AWS create data key.
        listener.reset()
        master_key = {
            'region':
            'us-east-1',
            'key':
            'arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-'
            '9f25-e30687b580d0'
        }
        aws_datakey_id = client_encryption.create_data_key(
            'aws', master_key=master_key, key_alt_names=['aws_altname'])
        self.assertBinaryUUID(aws_datakey_id)
        cmd = listener.results['started'][-1]
        self.assertEqual('insert', cmd.command_name)
        self.assertEqual({'w': 'majority'}, cmd.command.get('writeConcern'))
        docs = list(vault.find({'_id': aws_datakey_id}))
        self.assertEqual(len(docs), 1)
        self.assertEqual(docs[0]['masterKey']['provider'], 'aws')

        # AWS encrypt by key_id.
        aws_encrypted = client_encryption.encrypt(
            'hello aws',
            Algorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Deterministic,
            key_id=aws_datakey_id)
        self.assertEncrypted(aws_encrypted)
        client_encrypted.db.coll.insert_one({
            '_id': 'aws',
            'value': aws_encrypted
        })
        doc_decrypted = client_encrypted.db.coll.find_one({'_id': 'aws'})
        self.assertEqual(doc_decrypted['value'], 'hello aws')

        # AWS encrypt by key_alt_name.
        aws_encrypted_altname = client_encryption.encrypt(
            'hello aws',
            Algorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Deterministic,
            key_alt_name='aws_altname')
        self.assertEqual(aws_encrypted_altname, aws_encrypted)

        # Explicitly encrypting an auto encrypted field.
        msg = (r'Cannot encrypt element of type binData because schema '
               r'requires that type is one of: \[ string \]')
        with self.assertRaisesRegex(EncryptionError, msg):
            client_encrypted.db.coll.insert_one(
                {'encrypted_placeholder': local_encrypted})
Beispiel #23
0
 def test_init_requires_pymongocrypt(self):
     with self.assertRaises(ConfigurationError):
         AutoEncryptionOpts({}, 'keyvault.datakeys')
Beispiel #24
0
def get_connection(with_enc=False):
    """
    Returns instance of global pooled connection.
    The connection instance has automatic decryption enabled.
    -> MongoClient
    If with_enc=True this returns a ClientEncryption used for encryption fields along the connection
    -> MonogClient, ClientEncryption
    """
    global CONNECTION
    global CLIENT_ENC
    if CONNECTION is not None and CLIENT_ENC is not None:
        if with_enc:
            return CONNECTION, CLIENT_ENC
        else:
            return CONNECTION

    else:
        # Key must be 96 bytes
        local_master_key_raw = os.environ["SOFI_BIFROST_ENCRYPTION_KEY"]
        local_master_key = binascii.a2b_base64(local_master_key_raw.encode())

        kms_providers = {"local": {"key": local_master_key}}
        # The MongoDB namespace (db.collection) used to store
        # the encryption data keys.
        key_vault_namespace = SOFI_BIFROST_ENCRYPTION_NAMESPACE
        key_vault_db_name, key_name = (
            ENCRYPTION_DB,
            ENCRYPTION_KEY_NAME,
        )

        # bypass_auto_encryption=True disable automatic encryption but keeps
        # the automatic _decryption_ behavior. bypass_auto_encryption will
        # also disable spawning mongocryptd.
        auto_encryption_opts = AutoEncryptionOpts(kms_providers,
                                                  key_vault_namespace,
                                                  bypass_auto_encryption=True)

        client = (MongoClient(
            auto_encryption_opts=auto_encryption_opts) if DEBUG else
                  MongoClient(BIFROST_MONGO_CONN,
                              auto_encryption_opts=auto_encryption_opts))

        if not client.is_primary:
            logging.debug(
                "MongoDB client is not primary - getting the primary client")
            client = client.primary

        coll = client.test.coll

        # First time key setup. Index creation in mongo is idempotent
        key_vault = client[key_vault_db_name][key_name]
        key_vault.create_index(
            "keyAltNames",
            unique=True,
            partialFilterExpression={"keyAltNames": {
                "$exists": True
            }},
        )

        client_encryption = ClientEncryption(
            kms_providers,
            key_vault_namespace,
            # The MongoClient to use for reading/writing to the key vault.
            # This can be the same MongoClient used by the main application.
            client,
            # The CodecOptions class used for encrypting and decrypting.
            # This should be the same CodecOptions instance you have configured
            # on MongoClient, Database, or Collection.
            coll.codec_options,
        )

        existing = client[key_vault_db_name][key_name].find_one()
        if existing is None:
            client_encryption.create_data_key("local",
                                              key_alt_names=[key_name])

        CONNECTION = client
        CLIENT_ENC = client_encryption
        if with_enc:
            return CONNECTION, CLIENT_ENC
        else:
            return CONNECTION
Beispiel #25
0
 def test_corpus(self):
     opts = AutoEncryptionOpts(self.kms_providers(), 'keyvault.datakeys')
     self._test_corpus(opts)
Beispiel #26
0
 def test_kwargs(self):
     opts = AutoEncryptionOpts(KMS_PROVIDERS, 'keyvault.datakeys')
     client = MongoClient(auto_encryption_opts=opts, connect=False)
     self.addCleanup(client.close)
     self.assertEqual(get_client_opts(client).auto_encryption_opts, opts)
Beispiel #27
0
client = MongoClient("localhost", 27017)

# ** A LOCAL TEST KEY SHOULD NEVER BE USED IN PRODUCTION, ONLY FOR DEVELOPMENT **

# Test key material generated on Mac & Linux with: echo $(head -c 96 /dev/urandom | base64 | tr -d '\n')
local_key = 'CgOcoan3c/wm2c+WsOO6fXOUlJgd7SLQ1vl///aEFX6vXN9+7VOAP+iHKheZiYlB09ZS7CDcAQhlPeTeQNz03xiGbiCJJvl3uj4lnG+5i/udSLJAcwgtgtaedkFD0ROq'
key_bin = binary.Binary(base64.b64decode(local_key))
kms_providers = {'local': {'key': key_bin}}

print(
    "Please ensure mongocryptd is running (included in the standard enterprise download package)."
)

fle_opts = AutoEncryptionOpts(kms_providers,
                              "demoFLE.__keystore",
                              mongocryptd_bypass_spawn=True)

client = MongoClient("mongodb://localhost:27017/demoFLE",
                     auto_encryption_opts=fle_opts)

db = client.demoFLE

client_encryption = pymongo.encryption.ClientEncryption(
    kms_providers, "demoFLE.__keystore", client, OPTS)
client_encryption.create_data_key('local', key_alt_names=['pykey1'])

key1 = db.get_collection('__keystore').find_one({"keyAltNames":
                                                 "pykey1"})['_id']

client.close()
Beispiel #28
0
 def test_kwargs(self):
     opts = AutoEncryptionOpts({}, 'admin.datakeys')
     client = MongoClient(auto_encryption_opts=opts, connect=False)
     self.assertEqual(get_client_opts(client).auto_encryption_opts, opts)