@@ -409,6 +409,142 @@ Automatic encryption in Queryable Encryption is configured with an ``encrypted_f
409
409
In the above example, the ``firstName `` and ``lastName `` fields are
410
410
automatically encrypted and decrypted.
411
411
412
+ Explicit Queryable Encryption (Beta)
413
+ ````````````````````````````````````
414
+
415
+ PyMongo 4.2 brings beta support for Queryable Encryption with MongoDB 6.0.
416
+
417
+ Queryable Encryption is the second version of Client-Side Field Level Encryption.
418
+ Data is encrypted client-side. Queryable Encryption supports indexed encrypted fields,
419
+ which are further processed server-side.
420
+
421
+ You must have MongoDB 6.0rc8+ to preview the capability.
422
+
423
+ Until PyMongo 4.2 release is finalized, it can be installed using::
424
+
425
+ pip install "pymongo@git+ssh://[email protected] /mongodb/[email protected] #egg=pymongo[encryption]"
426
+
427
+ Additionally, ``libmongocrypt `` must be installed from `source <https://github.com/mongodb/libmongocrypt/blob/master/bindings/python/README.rst#installing-from-source >`_.
428
+
429
+ Explicit encryption in Queryable Encryption is performed using the ``encrypt `` and ``decrypt ``
430
+ methods. Automatic encryption (to allow the ``find_one `` to automatically decrypt) is configured
431
+ using an ``encrypted_fields `` mapping, as demonstrated by the following example::
432
+
433
+ import os
434
+
435
+ from pymongo import MongoClient
436
+ from pymongo.encryption import (Algorithm, AutoEncryptionOpts,
437
+ ClientEncryption, QueryType)
438
+
439
+
440
+ def main():
441
+ # This must be the same master key that was used to create
442
+ # the encryption key.
443
+ local_master_key = os.urandom(96)
444
+ kms_providers = {"local": {"key": local_master_key}}
445
+
446
+ # The MongoDB namespace (db.collection) used to store
447
+ # the encryption data keys.
448
+ key_vault_namespace = "encryption.__pymongoTestKeyVault"
449
+ key_vault_db_name, key_vault_coll_name = key_vault_namespace.split(".", 1)
450
+
451
+ # Set up the key vault (key_vault_namespace) for this example.
452
+ client = MongoClient()
453
+ key_vault = client[key_vault_db_name][key_vault_coll_name]
454
+
455
+ # Ensure that two data keys cannot share the same keyAltName.
456
+ key_vault.drop()
457
+ key_vault.create_index(
458
+ "keyAltNames",
459
+ unique=True,
460
+ partialFilterExpression={"keyAltNames": {"$exists": True}})
461
+
462
+ client_encryption = ClientEncryption(
463
+ kms_providers,
464
+ key_vault_namespace,
465
+ # The MongoClient to use for reading/writing to the key vault.
466
+ # This can be the same MongoClient used by the main application.
467
+ client,
468
+ # The CodecOptions class used for encrypting and decrypting.
469
+ # This should be the same CodecOptions instance you have configured
470
+ # on MongoClient, Database, or Collection.
471
+ client.codec_options)
472
+
473
+ # Create a new data key for the encryptedField.
474
+ indexed_key_id = client_encryption.create_data_key(
475
+ 'local')
476
+ unindexed_key_id = client_encryption.create_data_key(
477
+ 'local')
478
+
479
+ encrypted_fields = {
480
+ "escCollection": "enxcol_.default.esc",
481
+ "eccCollection": "enxcol_.default.ecc",
482
+ "ecocCollection": "enxcol_.default.ecoc",
483
+ "fields": [
484
+ {
485
+ "keyId": indexed_key_id,
486
+ "path": "encryptedIndexed",
487
+ "bsonType": "string",
488
+ "queries": {
489
+ "queryType": "equality"
490
+ }
491
+ },
492
+ {
493
+ "keyId": unindexed_key_id,
494
+ "path": "encryptedUnindexed",
495
+ "bsonType": "string",
496
+ }
497
+ ]
498
+ }
499
+
500
+ opts = AutoEncryptionOpts(
501
+ {"local": {"key": local_master_key}},
502
+ key_vault.full_name,
503
+ bypass_query_analysis=True,
504
+ key_vault_client=client,
505
+ )
506
+
507
+ # The MongoClient used to read/write application data.
508
+ encrypted_client = MongoClient(auto_encryption_opts=opts)
509
+ encrypted_client.drop_database("test")
510
+ db = encrypted_client.test
511
+
512
+ # Create the collection with encrypted fields.
513
+ coll = db.create_collection("coll", encrypted_fields=encrypted_fields)
514
+
515
+ # Create and encrypt an indexed and unindexed value.
516
+ val = "encrypted indexed value"
517
+ unindexed_val = "encrypted unindexed value"
518
+ insert_payload_indexed = client_encryption.encrypt(val, Algorithm.INDEXED, indexed_key_id)
519
+ insert_payload_unindexed = client_encryption.encrypt(unindexed_val, Algorithm.UNINDEXED,
520
+ unindexed_key_id)
521
+
522
+ # Insert the payloads.
523
+ coll.insert_one({
524
+ "encryptedIndexed": insert_payload_indexed,
525
+ "encryptedUnindexed": insert_payload_unindexed
526
+ })
527
+
528
+ # Encrypt our find payload using QueryType.EQUALITY.
529
+ # The value of "data_key_id" must be the same as used to encrypt the values
530
+ # above.
531
+ find_payload = client_encryption.encrypt(
532
+ val, Algorithm.INDEXED, indexed_key_id, query_type=QueryType.EQUALITY
533
+ )
534
+
535
+ # Find the document we inserted using the encrypted payload.
536
+ # The returned document is automatically decrypted.
537
+ doc = coll.find_one({"encryptedIndexed": find_payload})
538
+ print('Returned document: %s' % (doc,))
539
+
540
+ # Cleanup resources.
541
+ client_encryption.close()
542
+ encrypted_client.close()
543
+
544
+
545
+ if __name__ == "__main__":
546
+ main()
547
+
412
548
.. _explicit-client-side-encryption :
413
549
414
550
Explicit Encryption
0 commit comments