...

Text file src/go.mongodb.org/mongo-driver/testdata/client-side-encryption/README.rst

Documentation: go.mongodb.org/mongo-driver/testdata/client-side-encryption

     1============================
     2Client Side Encryption Tests
     3============================
     4
     5.. contents::
     6
     7----
     8
     9Introduction
    10============
    11
    12This document describes the format of the driver spec tests included in the JSON
    13and YAML files included in this directory.
    14
    15Additional prose tests, that are not represented in the spec tests, are described
    16and MUST be implemented by all drivers.
    17
    18Spec Test Format
    19================
    20
    21The spec tests format is an extension of `transactions spec tests <https://github.com/mongodb/specifications/blob/master/source/transactions/tests/README.rst>`_ with some additions:
    22
    23- A ``json_schema`` to set on the collection used for operations.
    24
    25- A ``key_vault_data`` of data that should be inserted in the key vault collection before each test.
    26
    27- Introduction ``autoEncryptOpts`` to `clientOptions`
    28
    29- Addition of `$db` to command in `command_started_event`
    30
    31- Addition of `$$type` to command_started_event and outcome.
    32
    33The semantics of `$$type` is that any actual value matching the BSON type indicated by the BSON type string is considered a match.
    34
    35For example, the following matches a command_started_event for an insert of a document where `random` must be of type ``binData``::
    36
    37  - command_started_event:
    38      command:
    39        insert: *collection_name
    40        documents:
    41          - { random: { $$type: "binData" } }
    42        ordered: true
    43      command_name: insert
    44
    45
    46The values of `$$type` correspond to `these documented string representations of BSON types <https://www.mongodb.com/docs/manual/reference/bson-types/>`_.
    47
    48
    49Each YAML file has the following keys:
    50
    51.. |txn| replace:: Unchanged from Transactions spec tests.
    52
    53- ``runOn`` |txn|
    54
    55- ``database_name`` |txn|
    56
    57- ``collection_name`` |txn|
    58
    59- ``data`` |txn|
    60
    61- ``json_schema`` A JSON Schema that should be set on the collection (using ``createCollection``) before each test run.
    62
    63- ``key_vault_data`` The data that should exist in the key vault collection under test before each test run.
    64
    65- ``tests``: An array of tests that are to be run independently of each other.
    66  Each test will have some or all of the following fields:
    67
    68  - ``description``: |txn|
    69
    70  - ``skipReason``: |txn|
    71
    72  - ``clientOptions``: Optional, parameters to pass to MongoClient().
    73
    74    - ``autoEncryptOpts``: Optional
    75
    76      - ``kmsProviders`` A dictionary of KMS providers to set on the key vault ("aws" or "local")
    77
    78        - ``aws`` The AWS KMS provider. An empty object. Drivers MUST fill in AWS credentials (`accessKeyId`, `secretAccessKey`) from the environment.
    79
    80        - ``azure`` The Azure KMS provider credentials. An empty object. Drivers MUST fill in Azure credentials (`tenantId`, `clientId`, and `clientSecret`) from the environment.
    81
    82        - ``gcp`` The GCP KMS provider credentials. An empty object. Drivers MUST fill in GCP credentials (`email`, `privateKey`) from the environment.
    83
    84        - ``local`` The local KMS provider.
    85
    86          - ``key`` A 96 byte local key.
    87
    88      - ``schemaMap``: Optional, a map from namespaces to local JSON schemas.
    89
    90      - ``keyVaultNamespace``: Optional, a namespace to the key vault collection. Defaults to "keyvault.datakeys".
    91
    92      - ``bypassAutoEncryption``: Optional, a boolean to indicate whether or not auto encryption should be bypassed. Defaults to ``false``.
    93
    94  - ``operations``: Array of documents, each describing an operation to be
    95    executed. Each document has the following fields:
    96
    97    - ``name``: |txn|
    98
    99    - ``object``: |txn|. Defaults to "collection" if omitted.
   100
   101    - ``collectionOptions``: |txn|
   102
   103    - ``command_name``: |txn|
   104
   105    - ``arguments``: |txn|
   106
   107    - ``result``: |txn|
   108
   109  - ``expectations``: |txn|
   110
   111  - ``outcome``: |txn|
   112
   113
   114
   115Use as integration tests
   116========================
   117
   118Do the following before running spec tests:
   119
   120- Start the mongocryptd process.
   121- Start a mongod process with **server version 4.1.9 or later**.
   122- Place credentials to an AWS IAM user (access key ID + secret access key) somewhere in the environment outside of tracked code. (If testing on evergreen, project variables are a good place).
   123
   124Load each YAML (or JSON) file using a Canonical Extended JSON parser.
   125
   126Then for each element in ``tests``:
   127
   128#. If the ``skipReason`` field is present, skip this test completely.
   129#. If the ``key_vault_data`` field is present:
   130
   131   #. Drop the ``keyvault.datakeys`` collection using writeConcern "majority".
   132   #. Insert the data specified into the ``keyvault.datakeys`` with write concern "majority".
   133
   134#. Create a MongoClient.
   135
   136#. Create a collection object from the MongoClient, using the ``database_name``
   137   and ``collection_name`` fields from the YAML file. Drop the collection
   138   with writeConcern "majority". If a ``json_schema`` is defined in the test,
   139   use the ``createCollection`` command to explicitly create the collection:
   140
   141   .. code:: typescript
   142
   143      {"create": <collection>, "validator": {"$jsonSchema": <json_schema>}}
   144
   145#. If the YAML file contains a ``data`` array, insert the documents in ``data``
   146   into the test collection, using writeConcern "majority".
   147
   148#. Create a **new** MongoClient using ``clientOptions``.
   149
   150   #. If ``autoEncryptOpts`` includes ``aws``, ``awsTemporary``, ``awsTemporaryNoSessionToken``,
   151      ``azure``, and/or ``gcp`` as a KMS provider, pass in credentials from the environment.
   152
   153      - ``awsTemporary``, and ``awsTemporaryNoSessionToken`` require temporary
   154        AWS credentials. These can be retrieved using the csfle `set-temp-creds.sh
   155        <https://github.com/mongodb-labs/drivers-evergreen-tools/tree/master/.evergreen/csfle>`_
   156        script.
   157
   158      - ``aws``, ``awsTemporary``, and ``awsTemporaryNoSessionToken`` are
   159        mutually exclusive.
   160
   161        ``aws`` should be substituted with:
   162
   163        .. code:: javascript
   164
   165           "aws": {
   166                "accessKeyId": <set from environment>,
   167                "secretAccessKey": <set from environment>
   168           }
   169
   170        ``awsTemporary`` should be substituted with:
   171
   172        .. code:: javascript
   173
   174           "aws": {
   175                "accessKeyId": <set from environment>,
   176                "secretAccessKey": <set from environment>
   177                "sessionToken": <set from environment>
   178           }
   179
   180        ``awsTemporaryNoSessionToken`` should be substituted with:
   181
   182        .. code:: javascript
   183
   184           "aws": {
   185               "accessKeyId": <set from environment>,
   186               "secretAccessKey": <set from environment>
   187           }
   188
   189        ``gcp`` should be substituted with:
   190
   191        .. code:: javascript
   192
   193           "gcp": {
   194               "email": <set from environment>,
   195               "privateKey": <set from environment>,
   196           }
   197
   198        ``azure`` should be substituted with:
   199
   200        .. code:: javascript
   201
   202           "azure": {
   203               "tenantId": <set from environment>,
   204               "clientId": <set from environment>,
   205               "clientSecret": <set from environment>,
   206           }
   207
   208        ``local`` should be substituted with:
   209
   210        .. code:: javascript
   211
   212           "local": { "key": <base64 decoding of LOCAL_MASTERKEY> }
   213
   214   #. If ``autoEncryptOpts`` does not include ``keyVaultNamespace``, default it
   215      to ``keyvault.datakeys``.
   216
   217#. For each element in ``operations``:
   218
   219   - Enter a "try" block or your programming language's closest equivalent.
   220   - Create a Database object from the MongoClient, using the ``database_name``
   221     field at the top level of the test file.
   222   - Create a Collection object from the Database, using the
   223     ``collection_name`` field at the top level of the test file.
   224     If ``collectionOptions`` is present create the Collection object with the
   225     provided options. Otherwise create the object with the default options.
   226   - Execute the named method on the provided ``object``, passing the
   227     arguments listed.
   228   - If the driver throws an exception / returns an error while executing this
   229     series of operations, store the error message and server error code.
   230   - If the result document has an "errorContains" field, verify that the
   231     method threw an exception or returned an error, and that the value of the
   232     "errorContains" field matches the error string. "errorContains" is a
   233     substring (case-insensitive) of the actual error message.
   234
   235     If the result document has an "errorCodeName" field, verify that the
   236     method threw a command failed exception or returned an error, and that
   237     the value of the "errorCodeName" field matches the "codeName" in the
   238     server error response.
   239
   240     If the result document has an "errorLabelsContain" field, verify that the
   241     method threw an exception or returned an error. Verify that all of the
   242     error labels in "errorLabelsContain" are present in the error or exception
   243     using the ``hasErrorLabel`` method.
   244
   245     If the result document has an "errorLabelsOmit" field, verify that the
   246     method threw an exception or returned an error. Verify that none of the
   247     error labels in "errorLabelsOmit" are present in the error or exception
   248     using the ``hasErrorLabel`` method.
   249   - If the operation returns a raw command response, eg from ``runCommand``,
   250     then compare only the fields present in the expected result document.
   251     Otherwise, compare the method's return value to ``result`` using the same
   252     logic as the CRUD Spec Tests runner.
   253
   254#. If the test includes a list of command-started events in ``expectations``,
   255   compare them to the actual command-started events using the
   256   same logic as the Command Monitoring Spec Tests runner.
   257
   258#. For each element in ``outcome``:
   259
   260   - If ``name`` is "collection", create a new MongoClient *without encryption*
   261     and verify that the test collection contains exactly the documents in the
   262     ``data`` array. Ensure this find reads the latest data by using
   263     **primary read preference** with **local read concern** even when the
   264     MongoClient is configured with another read preference or read concern.
   265
   266The spec test MUST be run with *and* without auth.
   267
   268Prose Tests
   269===========
   270
   271Tests for the ClientEncryption type are not included as part of the YAML tests.
   272
   273In the prose tests LOCAL_MASTERKEY refers to the following base64:
   274
   275.. code:: javascript
   276
   277  Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk
   278
   279Perform all applicable operations on key vault collections (e.g. inserting an example data key, or running a find command) with readConcern/writeConcern "majority".
   280
   281Data key and double encryption
   282~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   283
   284First, perform the setup.
   285
   286#. Create a MongoClient without encryption enabled (referred to as ``client``). Enable command monitoring to listen for command_started events.
   287
   288#. Using ``client``, drop the collections ``keyvault.datakeys`` and ``db.coll``.
   289
   290#. Create the following:
   291
   292   - A MongoClient configured with auto encryption (referred to as ``client_encrypted``)
   293   - A ``ClientEncryption`` object (referred to as ``client_encryption``)
   294
   295   Configure both objects with the following KMS providers:
   296
   297   .. code:: javascript
   298
   299      {
   300         "aws": {
   301            "accessKeyId": <set from environment>,
   302            "secretAccessKey": <set from environment>
   303         },
   304         "azure": {
   305            "tenantId": <set from environment>,
   306            "clientId": <set from environment>,
   307            "clientSecret": <set from environment>,
   308         },
   309            "gcp": {
   310            "email": <set from environment>,
   311            "privateKey": <set from environment>,
   312         }
   313         "local": { "key": <base64 decoding of LOCAL_MASTERKEY> }
   314      }
   315
   316   Configure both objects with ``keyVaultNamespace`` set to ``keyvault.datakeys``.
   317
   318   Configure the ``MongoClient`` with the following ``schema_map``:
   319
   320   .. code:: javascript
   321
   322      {
   323        "db.coll": {
   324          "bsonType": "object",
   325          "properties": {
   326            "encrypted_placeholder": {
   327              "encrypt": {
   328                "keyId": "/placeholder",
   329                "bsonType": "string",
   330                "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random"
   331              }
   332            }
   333          }
   334        }
   335      }
   336
   337   Configure ``client_encryption`` with the ``keyVaultClient`` of the previously created ``client``.
   338
   339For each KMS provider (``aws``, ``azure``, ``gcp``, and ``local``), referred to as ``provider_name``, run the following test.
   340
   341#. Call ``client_encryption.createDataKey()``.
   342
   343   - Set keyAltNames to ``["<provider_name>_altname"]``.
   344   - Set the masterKey document based on ``provider_name``.
   345
   346     For "aws":
   347
   348     .. code:: javascript
   349
   350        {
   351          region: "us-east-1",
   352          key: "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0"
   353        }
   354
   355     For "azure":
   356
   357     .. code:: javascript
   358
   359        {
   360          "keyVaultEndpoint": "key-vault-csfle.vault.azure.net",
   361          "keyName": "key-name-csfle"
   362        }
   363
   364     For "gcp":
   365
   366     .. code:: javascript
   367
   368        {
   369          "projectId": "devprod-drivers",
   370          "location": "global",
   371          "keyRing": "key-ring-csfle",
   372          "keyName": "key-name-csfle"
   373        }
   374
   375     For "local", do not set a masterKey document.
   376   - Expect a BSON binary with subtype 4 to be returned, referred to as ``datakey_id``.
   377   - Use ``client`` to run a ``find`` on ``keyvault.datakeys`` by querying with the ``_id`` set to the ``datakey_id``.
   378   - Expect that exactly one document is returned with the "masterKey.provider" equal to ``provider_name``.
   379   - Check that ``client`` captured a command_started event for the ``insert`` command containing a majority writeConcern.
   380
   381#. Call ``client_encryption.encrypt()`` with the value "hello <provider_name>", the algorithm ``AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic``, and the ``key_id`` of ``datakey_id``.
   382
   383   - Expect the return value to be a BSON binary subtype 6, referred to as ``encrypted``.
   384   - Use ``client_encrypted`` to insert ``{ _id: "<provider_name>", "value": <encrypted> }`` into ``db.coll``.
   385   - Use ``client_encrypted`` to run a find querying with ``_id`` of "<provider_name>" and expect ``value`` to be "hello <provider_name>".
   386
   387#. Call ``client_encryption.encrypt()`` with the value "hello <provider_name>", the algorithm ``AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic``, and the ``key_alt_name`` of ``<provider_name>_altname``.
   388
   389   - Expect the return value to be a BSON binary subtype 6. Expect the value to exactly match the value of ``encrypted``.
   390
   391#. Test explicit encrypting an auto encrypted field.
   392
   393   - Use ``client_encrypted`` to attempt to insert ``{ "encrypted_placeholder": <encrypted> }``
   394   - Expect an exception to be thrown, since this is an attempt to auto encrypt an already encrypted value.
   395
   396
   397
   398External Key Vault Test
   399~~~~~~~~~~~~~~~~~~~~~~~
   400
   401Run the following tests twice, parameterized by a boolean ``withExternalKeyVault``.
   402
   403#. Create a MongoClient without encryption enabled (referred to as ``client``).
   404
   405#. Using ``client``, drop the collections ``keyvault.datakeys`` and ``db.coll``.
   406   Insert the document `external/external-key.json <../external/external-key.json>`_ into ``keyvault.datakeys``.
   407
   408#. Create the following:
   409
   410   - A MongoClient configured with auto encryption (referred to as ``client_encrypted``)
   411   - A ``ClientEncryption`` object (referred to as ``client_encryption``)
   412
   413   Configure both objects with the ``local`` KMS providers as follows:
   414
   415   .. code:: javascript
   416
   417      { "local": { "key": <base64 decoding of LOCAL_MASTERKEY> } }
   418
   419   Configure both objects with ``keyVaultNamespace`` set to ``keyvault.datakeys``.
   420
   421   Configure ``client_encrypted`` to use the schema `external/external-schema.json <../external/external-schema.json>`_  for ``db.coll`` by setting a schema map like: ``{ "db.coll": <contents of external-schema.json>}``
   422
   423   If ``withExternalKeyVault == true``, configure both objects with an external key vault client. The external client MUST connect to the same
   424   MongoDB cluster that is being tested against, except it MUST use the username ``fake-user`` and password ``fake-pwd``.
   425
   426#. Use ``client_encrypted`` to insert the document ``{"encrypted": "test"}`` into ``db.coll``.
   427   If ``withExternalKeyVault == true``, expect an authentication exception to be thrown. Otherwise, expect the insert to succeed.
   428
   429#. Use ``client_encryption`` to explicitly encrypt the string ``"test"`` with key ID ``LOCALAAAAAAAAAAAAAAAAA==`` and deterministic algorithm.
   430   If ``withExternalKeyVault == true``, expect an authentication exception to be thrown. Otherwise, expect the insert to succeed.
   431
   432
   433BSON size limits and batch splitting
   434~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   435
   436First, perform the setup.
   437
   438#. Create a MongoClient without encryption enabled (referred to as ``client``).
   439
   440#. Using ``client``, drop and create the collection ``db.coll`` configured with the included JSON schema `limits/limits-schema.json <../limits/limits-schema.json>`_.
   441
   442#. Using ``client``, drop the collection ``keyvault.datakeys``. Insert the document `limits/limits-key.json <../limits/limits-key.json>`_
   443
   444#. Create a MongoClient configured with auto encryption (referred to as ``client_encrypted``)
   445
   446   Configure with the ``local`` KMS provider as follows:
   447
   448   .. code:: javascript
   449
   450      { "local": { "key": <base64 decoding of LOCAL_MASTERKEY> } }
   451
   452   Configure with the ``keyVaultNamespace`` set to ``keyvault.datakeys``.
   453
   454Using ``client_encrypted`` perform the following operations:
   455
   456#. Insert ``{ "_id": "over_2mib_under_16mib", "unencrypted": <the string "a" repeated 2097152 times> }``.
   457
   458   Expect this to succeed since this is still under the ``maxBsonObjectSize`` limit.
   459
   460#. Insert the document `limits/limits-doc.json <../limits/limits-doc.json>`_ concatenated with ``{ "_id": "encryption_exceeds_2mib", "unencrypted": < the string "a" repeated (2097152 - 2000) times > }``
   461   Note: limits-doc.json is a 1005 byte BSON document that encrypts to a ~10,000 byte document.
   462
   463   Expect this to succeed since after encryption this still is below the normal maximum BSON document size.
   464   Note, before auto encryption this document is under the 2 MiB limit. After encryption it exceeds the 2 MiB limit, but does NOT exceed the 16 MiB limit.
   465
   466#. Bulk insert the following:
   467
   468   - ``{ "_id": "over_2mib_1", "unencrypted": <the string "a" repeated (2097152) times> }``
   469
   470   - ``{ "_id": "over_2mib_2", "unencrypted": <the string "a" repeated (2097152) times> }``
   471
   472   Expect the bulk write to succeed and split after first doc (i.e. two inserts occur). This may be verified using `command monitoring <https://github.com/mongodb/specifications/tree/master/source/command-monitoring/command-monitoring.rst>`_.
   473
   474#. Bulk insert the following:
   475
   476   - The document `limits/limits-doc.json <../limits/limits-doc.json>`_ concatenated with ``{ "_id": "encryption_exceeds_2mib_1", "unencrypted": < the string "a" repeated (2097152 - 2000) times > }``
   477
   478   - The document `limits/limits-doc.json <../limits/limits-doc.json>`_ concatenated with ``{ "_id": "encryption_exceeds_2mib_2", "unencrypted": < the string "a" repeated (2097152 - 2000) times > }``
   479
   480   Expect the bulk write to succeed and split after first doc (i.e. two inserts occur). This may be verified using `command monitoring <https://github.com/mongodb/specifications/tree/master/source/command-monitoring/command-monitoring.rst>`_.
   481
   482#. Insert ``{ "_id": "under_16mib", "unencrypted": <the string "a" repeated 16777216 - 2000 times>``.
   483
   484   Expect this to succeed since this is still (just) under the ``maxBsonObjectSize`` limit.
   485
   486#. Insert the document `limits/limits-doc.json <../limits/limits-doc.json>`_ concatenated with ``{ "_id": "encryption_exceeds_16mib", "unencrypted": < the string "a" repeated (16777216 - 2000) times > }``
   487
   488   Expect this to fail since encryption results in a document exceeding the ``maxBsonObjectSize`` limit.
   489
   490Optionally, if it is possible to mock the maxWriteBatchSize (i.e. the maximum number of documents in a batch) test that setting maxWriteBatchSize=1 and inserting the two documents ``{ "_id": "a" }, { "_id": "b" }`` with ``client_encrypted`` splits the operation into two inserts.
   491
   492
   493Views are prohibited
   494~~~~~~~~~~~~~~~~~~~~
   495
   496#. Create a MongoClient without encryption enabled (referred to as ``client``).
   497
   498#. Using ``client``, drop and create a view named ``db.view`` with an empty pipeline. E.g. using the command ``{ "create": "view", "viewOn": "coll" }``.
   499
   500#. Create a MongoClient configured with auto encryption (referred to as ``client_encrypted``)
   501
   502   Configure with the ``local`` KMS provider as follows:
   503
   504   .. code:: javascript
   505
   506      { "local": { "key": <base64 decoding of LOCAL_MASTERKEY> } }
   507
   508   Configure with the ``keyVaultNamespace`` set to ``keyvault.datakeys``.
   509
   510#. Using ``client_encrypted``, attempt to insert a document into ``db.view``. Expect an exception to be thrown containing the message: "cannot auto encrypt a view".
   511
   512
   513Corpus Test
   514~~~~~~~~~~~
   515
   516The corpus test exhaustively enumerates all ways to encrypt all BSON value types. Note, the test data includes BSON binary subtype 4 (or standard UUID), which MUST be decoded and encoded as subtype 4. Run the test as follows.
   517
   5181. Create a MongoClient without encryption enabled (referred to as ``client``).
   519
   5202. Using ``client``, drop and create the collection ``db.coll`` configured with the included JSON schema `corpus/corpus-schema.json <../corpus/corpus-schema.json>`_.
   521
   5223. Using ``client``, drop the collection ``keyvault.datakeys``. Insert the documents `corpus/corpus-key-local.json <../corpus/corpus-key-local.json>`_, `corpus/corpus-key-aws.json <../corpus/corpus-key-aws.json>`_, `corpus/corpus-key-azure.json <../corpus/corpus-key-azure.json>`_, and `corpus/corpus-key-gcp.json <../corpus/corpus-key-gcp.json>`_.
   523
   5244. Create the following:
   525
   526   - A MongoClient configured with auto encryption (referred to as ``client_encrypted``)
   527   - A ``ClientEncryption`` object (referred to as ``client_encryption``)
   528
   529   Configure both objects with ``aws``, ``azure``, ``gcp``, and ``local`` KMS providers as follows:
   530
   531   .. code:: javascript
   532
   533      {
   534          "aws": { <AWS credentials> },
   535          "azure": { <Azure credentials> },
   536          "gcp": { <GCP credentials> },
   537          "local": { "key": <base64 decoding of LOCAL_MASTERKEY> }
   538      }
   539
   540   Where LOCAL_MASTERKEY is the following base64:
   541
   542   .. code:: javascript
   543
   544      Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk
   545
   546   Configure both objects with ``keyVaultNamespace`` set to ``keyvault.datakeys``.
   547
   5485. Load `corpus/corpus.json <../corpus/corpus.json>`_ to a variable named ``corpus``. The corpus contains subdocuments with the following fields:
   549
   550   - ``kms`` is either ``aws``, ``azure``, ``gcp``, or ``local``
   551   - ``type`` is a BSON type string `names coming from here <https://www.mongodb.com/docs/manual/reference/operator/query/type/>`_)
   552   - ``algo`` is either ``rand`` or ``det`` for random or deterministic encryption
   553   - ``method`` is either ``auto``, for automatic encryption or ``explicit`` for  explicit encryption
   554   - ``identifier`` is either ``id`` or ``altname`` for the key identifier
   555   - ``allowed`` is a boolean indicating whether the encryption for the given parameters is permitted.
   556   - ``value`` is the value to be tested.
   557
   558   Create a new BSON document, named ``corpus_copied``.
   559   Iterate over each field of ``corpus``.
   560
   561   - If the field name is ``_id``, ``altname_aws``, ``altname_local``, ``altname_azure``, or ``altname_gcp``, copy the field to ``corpus_copied``.
   562   - If ``method`` is ``auto``, copy the field to ``corpus_copied``.
   563   - If ``method`` is ``explicit``, use ``client_encryption`` to explicitly encrypt the value.
   564
   565     - Encrypt with the algorithm described by ``algo``.
   566     - If ``identifier`` is ``id``
   567
   568       - If ``kms`` is ``local`` set the key_id to the UUID with base64 value ``LOCALAAAAAAAAAAAAAAAAA==``.
   569       - If ``kms`` is ``aws`` set the key_id to the UUID with base64 value ``AWSAAAAAAAAAAAAAAAAAAA==``.
   570       - If ``kms`` is ``azure`` set the key_id to the UUID with base64 value ``AZUREAAAAAAAAAAAAAAAAA==``.
   571       - If ``kms`` is ``gcp`` set the key_id to the UUID with base64 value ``GCPAAAAAAAAAAAAAAAAAAA==``.
   572
   573     - If ``identifier`` is ``altname``
   574
   575       - If ``kms`` is ``local`` set the key_alt_name to "local".
   576       - If ``kms`` is ``aws`` set the key_alt_name to "aws".
   577       - If ``kms`` is ``azure`` set the key_alt_name to "azure".
   578       - If ``kms`` is ``gcp`` set the key_alt_name to "gcp".
   579
   580     If ``allowed`` is true, copy the field and encrypted value to ``corpus_copied``.
   581     If ``allowed`` is false. verify that an exception is thrown. Copy the unencrypted value to to ``corpus_copied``.
   582
   583
   5846. Using ``client_encrypted``, insert ``corpus_copied`` into ``db.coll``.
   585
   5867. Using ``client_encrypted``, find the inserted document from ``db.coll`` to a variable named ``corpus_decrypted``. Since it should have been automatically decrypted, assert the document exactly matches ``corpus``.
   587
   5888. Load `corpus/corpus_encrypted.json <../corpus/corpus-encrypted.json>`_ to a variable named ``corpus_encrypted_expected``.
   589   Using ``client`` find the inserted document from ``db.coll`` to a variable named ``corpus_encrypted_actual``.
   590
   591   Iterate over each field of ``corpus_encrypted_expected`` and check the following:
   592
   593   - If the ``algo`` is ``det``, that the value equals the value of the corresponding field in ``corpus_encrypted_actual``.
   594   - If the ``algo`` is ``rand`` and ``allowed`` is true, that the value does not equal the value of the corresponding field in ``corpus_encrypted_actual``.
   595   - If ``allowed`` is true, decrypt the value with ``client_encryption``. Decrypt the value of the corresponding field of ``corpus_encrypted`` and validate that they are both equal.
   596   - If ``allowed`` is false, validate the value exactly equals the value of the corresponding field of ``corpus`` (neither was encrypted).
   597
   5989. Repeat steps 1-8 with a local JSON schema. I.e. amend step 4 to configure the schema on ``client_encrypted`` with the ``schema_map`` option.
   599
   600Custom Endpoint Test
   601~~~~~~~~~~~~~~~~~~~~
   602
   603Setup
   604`````
   605
   606For each test cases, start by creating two ``ClientEncryption`` objects. Recreate the ``ClientEncryption`` objects for each test case.
   607
   608Create a ``ClientEncryption`` object (referred to as ``client_encryption``)
   609
   610Configure with ``keyVaultNamespace`` set to ``keyvault.datakeys``, and a default MongoClient as the ``keyVaultClient``.
   611
   612Configure with KMS providers as follows:
   613
   614.. code:: javascript
   615
   616   {
   617         "aws": {
   618            "accessKeyId": <set from environment>,
   619            "secretAccessKey": <set from environment>
   620         },
   621         "azure": {
   622            "tenantId": <set from environment>,
   623            "clientId": <set from environment>,
   624            "clientSecret": <set from environment>,
   625            "identityPlatformEndpoint": "login.microsoftonline.com:443"
   626         },
   627            "gcp": {
   628            "email": <set from environment>,
   629            "privateKey": <set from environment>,
   630            "endpoint": "oauth2.googleapis.com:443"
   631         }
   632   }
   633
   634Create a ``ClientEncryption`` object (referred to as ``client_encryption_invalid``)
   635
   636Configure with ``keyVaultNamespace`` set to ``keyvault.datakeys``, and a default MongoClient as the ``keyVaultClient``.
   637
   638Configure with KMS providers as follows:
   639
   640.. code:: javascript
   641
   642   {
   643         "azure": {
   644            "tenantId": <set from environment>,
   645            "clientId": <set from environment>,
   646            "clientSecret": <set from environment>,
   647            "identityPlatformEndpoint": "example.com:443"
   648         },
   649            "gcp": {
   650            "email": <set from environment>,
   651            "privateKey": <set from environment>,
   652            "endpoint": "example.com:443"
   653         }
   654   }
   655
   656Test cases
   657``````````
   658
   6591. Call `client_encryption.createDataKey()` with "aws" as the provider and the following masterKey:
   660
   661   .. code:: javascript
   662
   663      {
   664        region: "us-east-1",
   665        key: "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0"
   666      }
   667
   668   Expect this to succeed. Use the returned UUID of the key to explicitly encrypt and decrypt the string "test" to validate it works.
   669
   6702. Call `client_encryption.createDataKey()` with "aws" as the provider and the following masterKey:
   671
   672   .. code:: javascript
   673
   674      {
   675        region: "us-east-1",
   676        key: "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0",
   677        endpoint: "kms.us-east-1.amazonaws.com"
   678      }
   679
   680   Expect this to succeed. Use the returned UUID of the key to explicitly encrypt and decrypt the string "test" to validate it works.
   681
   6823. Call `client_encryption.createDataKey()` with "aws" as the provider and the following masterKey:
   683
   684   .. code:: javascript
   685
   686      {
   687        region: "us-east-1",
   688        key: "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0",
   689        endpoint: "kms.us-east-1.amazonaws.com:443"
   690      }
   691
   692   Expect this to succeed. Use the returned UUID of the key to explicitly encrypt and decrypt the string "test" to validate it works.
   693
   6944. Call `client_encryption.createDataKey()` with "aws" as the provider and the following masterKey:
   695
   696   .. code:: javascript
   697
   698      {
   699        region: "us-east-1",
   700        key: "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0",
   701        endpoint: "kms.us-east-1.amazonaws.com:12345"
   702      }
   703
   704   Expect this to fail with a socket connection error.
   705
   7065. Call `client_encryption.createDataKey()` with "aws" as the provider and the following masterKey:
   707
   708   .. code:: javascript
   709
   710      {
   711        region: "us-east-1",
   712        key: "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0",
   713        endpoint: "kms.us-east-2.amazonaws.com"
   714      }
   715
   716   Expect this to fail with an exception with a message containing the string: "us-east-1"
   717
   7186. Call `client_encryption.createDataKey()` with "aws" as the provider and the following masterKey:
   719
   720   .. code:: javascript
   721
   722      {
   723        region: "us-east-1",
   724        key: "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0",
   725        endpoint: "example.com"
   726      }
   727
   728   Expect this to fail with an exception with a message containing the string: "parse error"
   729
   7307. Call `client_encryption.createDataKey()` with "azure" as the provider and the following masterKey:
   731
   732   .. code:: javascript
   733
   734      {
   735         "keyVaultEndpoint": "key-vault-csfle.vault.azure.net",
   736         "keyName": "key-name-csfle"
   737      }
   738
   739   Expect this to succeed. Use the returned UUID of the key to explicitly encrypt and decrypt the string "test" to validate it works.
   740
   741   Call ``client_encryption_invalid.createDataKey()`` with the same masterKey. Expect this to fail with an exception with a message containing the string: "parse error".
   742
   7438. Call `client_encryption.createDataKey()` with "gcp" as the provider and the following masterKey:
   744
   745   .. code:: javascript
   746
   747      {
   748        "projectId": "devprod-drivers",
   749        "location": "global",
   750        "keyRing": "key-ring-csfle",
   751        "keyName": "key-name-csfle",
   752        "endpoint": "cloudkms.googleapis.com:443"
   753      }
   754
   755   Expect this to succeed. Use the returned UUID of the key to explicitly encrypt and decrypt the string "test" to validate it works.
   756
   757   Call ``client_encryption_invalid.createDataKey()`` with the same masterKey. Expect this to fail with an exception with a message containing the string: "parse error".
   758
   7599. Call `client_encryption.createDataKey()` with "gcp" as the provider and the following masterKey:
   760
   761   .. code:: javascript
   762
   763      {
   764        "projectId": "devprod-drivers",
   765        "location": "global",
   766        "keyRing": "key-ring-csfle",
   767        "keyName": "key-name-csfle",
   768        "endpoint": "example.com:443"
   769      }
   770
   771   Expect this to fail with an exception with a message containing the string: "Invalid KMS response".
   772
   773Bypass spawning mongocryptd
   774~~~~~~~~~~~~~~~~~~~~~~~~~~~
   775
   776Via mongocryptdBypassSpawn
   777``````````````````````````
   778
   779The following tests that setting ``mongocryptdBypassSpawn=true`` really does bypass spawning mongocryptd.
   780
   781#. Create a MongoClient configured with auto encryption (referred to as ``client_encrypted``)
   782
   783   Configure the required options. Use the ``local`` KMS provider as follows:
   784
   785   .. code:: javascript
   786
   787      { "local": { "key": <base64 decoding of LOCAL_MASTERKEY> } }
   788
   789   Configure with the ``keyVaultNamespace`` set to ``keyvault.datakeys``.
   790
   791   Configure ``client_encrypted`` to use the schema `external/external-schema.json <../external/external-schema.json>`_  for ``db.coll`` by setting a schema map like: ``{ "db.coll": <contents of external-schema.json>}``
   792
   793   Configure the following ``extraOptions``:
   794
   795   .. code:: javascript
   796
   797      {
   798        "mongocryptdBypassSpawn": true
   799        "mongocryptdURI": "mongodb://localhost:27021/db?serverSelectionTimeoutMS=1000",
   800        "mongocryptdSpawnArgs": [ "--pidfilepath=bypass-spawning-mongocryptd.pid", "--port=27021"]
   801      }
   802
   803   Drivers MAY pass a different port if they expect their testing infrastructure to be using port 27021. Pass a port that should be free.
   804
   805#. Use ``client_encrypted`` to insert the document ``{"encrypted": "test"}`` into ``db.coll``. Expect a server selection error propagated from the internal MongoClient failing to connect to mongocryptd on port 27021.
   806
   807Via bypassAutoEncryption
   808````````````````````````
   809
   810The following tests that setting ``bypassAutoEncryption=true`` really does bypass spawning mongocryptd.
   811
   812#. Create a MongoClient configured with auto encryption (referred to as ``client_encrypted``)
   813
   814   Configure the required options. Use the ``local`` KMS provider as follows:
   815
   816   .. code:: javascript
   817
   818      { "local": { "key": <base64 decoding of LOCAL_MASTERKEY> } }
   819
   820   Configure with the ``keyVaultNamespace`` set to ``keyvault.datakeys``.
   821
   822   Configure with ``bypassAutoEncryption=true``.
   823
   824   Configure the following ``extraOptions``:
   825
   826   .. code:: javascript
   827
   828      {
   829        "mongocryptdSpawnArgs": [ "--pidfilepath=bypass-spawning-mongocryptd.pid", "--port=27021"]
   830      }
   831
   832   Drivers MAY pass a different value to ``--port`` if they expect their testing infrastructure to be using port 27021. Pass a port that should be free.
   833
   834#. Use ``client_encrypted`` to insert the document ``{"unencrypted": "test"}`` into ``db.coll``. Expect this to succeed.
   835
   836#. Validate that mongocryptd was not spawned. Create a MongoClient to localhost:27021 (or whatever was passed via ``--port``) with serverSelectionTimeoutMS=1000. Run a handshake command and ensure it fails with a server selection timeout.
   837
   838Deadlock tests
   839~~~~~~~~~~~~~~
   840
   841.. _Connection Monitoring and Pooling: /source/connection-monitoring-and-pooling/connection-monitoring-and-pooling.rst
   842
   843The following tests only apply to drivers that have implemented a connection pool (see the `Connection Monitoring and Pooling`_ specification).
   844
   845There are multiple parameterized test cases. Before each test case, perform the setup.
   846
   847Setup
   848`````
   849
   850Create a ``MongoClient`` for setup operations named ``client_test``.
   851
   852Create a ``MongoClient`` for key vault operations with ``maxPoolSize=1`` named ``client_keyvault``. Capture command started events.
   853
   854Using ``client_test``, drop the collections ``keyvault.datakeys`` and ``db.coll``.
   855
   856Insert the document `external/external-key.json <../external/external-key.json>`_ into ``keyvault.datakeys`` with majority write concern.
   857
   858Create a collection ``db.coll`` configured with a JSON schema `external/external-schema.json <../external/external-schema.json>`_ as the validator, like so:
   859
   860.. code:: typescript
   861
   862   {"create": "coll", "validator": {"$jsonSchema": <json_schema>}}
   863
   864Create a ``ClientEncryption`` object, named ``client_encryption`` configured with:
   865- ``keyVaultClient``=``client_test``
   866- ``keyVaultNamespace``="keyvault.datakeys"
   867- ``kmsProviders``=``{ "local": { "key": <base64 decoding of LOCAL_MASTERKEY> } }``
   868
   869Use ``client_encryption`` to encrypt the value "string0" with ``algorithm``="AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" and ``keyAltName``="local". Store the result in a variable named ``ciphertext``.
   870
   871Proceed to run the test case.
   872
   873Each test case configures a ``MongoClient`` with automatic encryption (named ``client_encrypted``).
   874
   875Each test must assert the number of unique ``MongoClient``s created. This can be accomplished by capturing ``TopologyOpeningEvent``, or by checking command started events for a client identifier (not possible in all drivers).
   876
   877Running a test case
   878```````````````````
   879- Create a ``MongoClient`` named ``client_encrypted`` configured as follows:
   880   - Set ``AutoEncryptionOpts``:
   881      - ``keyVaultNamespace="keyvault.datakeys"``
   882      - ``kmsProviders``=``{ "local": { "key": <base64 decoding of LOCAL_MASTERKEY> } }``
   883      - Append ``TestCase.AutoEncryptionOpts`` (defined below)
   884   - Capture command started events.
   885   - Set ``maxPoolSize=TestCase.MaxPoolSize``
   886- If the testcase sets ``AutoEncryptionOpts.bypassAutoEncryption=true``:
   887   - Use ``client_test`` to insert ``{ "_id": 0, "encrypted": <ciphertext> }`` into ``db.coll``.
   888- Otherwise:
   889   - Use ``client_encrypted`` to insert ``{ "_id": 0, "encrypted": "string0" }``.
   890- Use ``client_encrypted`` to run a ``findOne`` operation on ``db.coll``, with the filter ``{ "_id": 0 }``.
   891- Expect the result to be ``{ "_id": 0, "encrypted": "string0" }``.
   892- Check captured events against ``TestCase.Expectations``.
   893- Check the number of unique ``MongoClient``s created is equal to ``TestCase.ExpectedNumberOfClients``.
   894
   895Case 1
   896``````
   897- MaxPoolSize: 1
   898- AutoEncryptionOpts:
   899   - bypassAutoEncryption=false
   900   - keyVaultClient=unset
   901- Expectations:
   902   - Expect ``client_encrypted`` to have captured four ``CommandStartedEvent``:
   903      - a listCollections to "db".
   904      - a find on "keyvault".
   905      - an insert on "db".
   906      - a find on "db"
   907- ExpectedNumberOfClients: 2
   908
   909Case 2
   910``````
   911- MaxPoolSize: 1
   912- AutoEncryptionOpts:
   913   - bypassAutoEncryption=false
   914   - keyVaultClient=client_keyvault
   915- Expectations:
   916   - Expect ``client_encrypted`` to have captured three ``CommandStartedEvent``:
   917      - a listCollections to "db".
   918      - an insert on "db".
   919      - a find on "db"
   920   - Expect ``client_keyvault`` to have captured one ``CommandStartedEvent``:
   921      - a find on "keyvault".
   922- ExpectedNumberOfClients: 2
   923
   924Case 3
   925``````
   926- MaxPoolSize: 1
   927- AutoEncryptionOpts:
   928   - bypassAutoEncryption=true
   929   - keyVaultClient=unset
   930- Expectations:
   931   - Expect ``client_encrypted`` to have captured three ``CommandStartedEvent``:
   932      - a find on "db"
   933      - a find on "keyvault".
   934- ExpectedNumberOfClients: 2
   935
   936Case 4
   937``````
   938- MaxPoolSize: 1
   939- AutoEncryptionOpts:
   940   - bypassAutoEncryption=true
   941   - keyVaultClient=client_keyvault
   942- Expectations:
   943   - Expect ``client_encrypted`` to have captured two ``CommandStartedEvent``:
   944      - a find on "db"
   945   - Expect ``client_keyvault`` to have captured one ``CommandStartedEvent``:
   946      - a find on "keyvault".
   947- ExpectedNumberOfClients: 1
   948
   949Case 5
   950``````
   951Drivers that do not support an unlimited maximum pool size MUST skip this test.
   952
   953- MaxPoolSize: 0
   954- AutoEncryptionOpts:
   955   - bypassAutoEncryption=false
   956   - keyVaultClient=unset
   957- Expectations:
   958   - Expect ``client_encrypted`` to have captured five ``CommandStartedEvent``:
   959      - a listCollections to "db".
   960      - a listCollections to "keyvault".
   961      - a find on "keyvault".
   962      - an insert on "db".
   963      - a find on "db"
   964- ExpectedNumberOfClients: 1
   965
   966Case 6
   967``````
   968Drivers that do not support an unlimited maximum pool size MUST skip this test.
   969
   970- MaxPoolSize: 0
   971- AutoEncryptionOpts:
   972   - bypassAutoEncryption=false
   973   - keyVaultClient=client_keyvault
   974- Expectations:
   975   - Expect ``client_encrypted`` to have captured three ``CommandStartedEvent``:
   976      - a listCollections to "db".
   977      - an insert on "db".
   978      - a find on "db"
   979   - Expect ``client_keyvault`` to have captured one ``CommandStartedEvent``:
   980      - a find on "keyvault".
   981- ExpectedNumberOfClients: 1
   982
   983Case 7
   984``````
   985Drivers that do not support an unlimited maximum pool size MUST skip this test.
   986
   987- MaxPoolSize: 0
   988- AutoEncryptionOpts:
   989   - bypassAutoEncryption=true
   990   - keyVaultClient=unset
   991- Expectations:
   992   - Expect ``client_encrypted`` to have captured three ``CommandStartedEvent``:
   993      - a find on "db"
   994      - a find on "keyvault".
   995- ExpectedNumberOfClients: 1
   996
   997Case 8
   998``````
   999Drivers that do not support an unlimited maximum pool size MUST skip this test.
  1000
  1001- MaxPoolSize: 0
  1002- AutoEncryptionOpts:
  1003   - bypassAutoEncryption=true
  1004   - keyVaultClient=client_keyvault
  1005- Expectations:
  1006   - Expect ``client_encrypted`` to have captured two ``CommandStartedEvent``:
  1007      - a find on "db"
  1008   - Expect ``client_keyvault`` to have captured one ``CommandStartedEvent``:
  1009      - a find on "keyvault".
  1010- ExpectedNumberOfClients: 1
  1011
  1012KMS TLS Tests
  1013~~~~~~~~~~~~~
  1014
  1015.. _ca.pem: https://github.com/mongodb-labs/drivers-evergreen-tools/blob/master/.evergreen/x509gen/ca.pem
  1016.. _expired.pem: https://github.com/mongodb-labs/drivers-evergreen-tools/blob/master/.evergreen/x509gen/expired.pem
  1017.. _wrong-host.pem: https://github.com/mongodb-labs/drivers-evergreen-tools/blob/master/.evergreen/x509gen/wrong-host.pem
  1018
  1019The following tests that connections to KMS servers with TLS verify peer certificates.
  1020
  1021The two tests below make use of mock KMS servers which can be run on Evergreen using `the mock KMS server script <https://github.com/mongodb-labs/drivers-evergreen-tools/blob/master/.evergreen/csfle/kms_http_server.py>`_.
  1022Drivers can set up their local Python environment for the mock KMS server by running `the virtualenv activation script <https://github.com/mongodb-labs/drivers-evergreen-tools/blob/master/.evergreen/csfle/activate_venv.sh>`_.
  1023
  1024To start a mock KMS server on port 8000 with `ca.pem`_ as a CA file and `expired.pem`_ as a cert file, run the following commands from the ``.evergreen/csfle`` directory.
  1025
  1026.. code::
  1027
  1028   . ./activate_venv.sh
  1029   python -u kms_http_server.py --ca_file ../x509gen/ca.pem --cert_file ../x509gen/expired.pem --port 8000
  1030
  1031Setup
  1032`````
  1033
  1034For both tests, do the following:
  1035
  1036#. Start a ``mongod`` process with **server version 4.1.9 or later**.
  1037
  1038#. Create a ``MongoClient`` (referred to as ``client_encrypted``) for key vault operations with ``keyVaultNamespace`` set to ``keyvault.datakeys``:
  1039
  1040Invalid KMS Certificate
  1041```````````````````````
  1042
  1043#. Start a mock KMS server on port 8000 with `ca.pem`_ as a CA file and `expired.pem`_ as a cert file.
  1044
  1045#. Call ``client_encrypted.createDataKey()`` with "aws" as the provider and the following masterKey:
  1046
  1047   .. code:: javascript
  1048
  1049      {
  1050         "region": "us-east-1",
  1051         "key": "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0",
  1052         "endpoint": "127.0.0.1:8000",
  1053      }
  1054
  1055   Expect this to fail with an exception with a message referencing an expired certificate. This message will be language dependent.
  1056   In Python, this message is "certificate verify failed: certificate has expired". In Go, this message is
  1057   "certificate has expired or is not yet valid".
  1058
  1059Invalid Hostname in KMS Certificate
  1060```````````````````````````````````
  1061
  1062#. Start a mock KMS server on port 8001 with `ca.pem`_ as a CA file and `wrong-host.pem`_ as a cert file.
  1063
  1064#. Call ``client_encrypted.createDataKey()`` with "aws" as the provider and the following masterKey:
  1065
  1066   .. code:: javascript
  1067
  1068      {
  1069         "region": "us-east-1",
  1070         "key": "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0",
  1071         "endpoint": "127.0.0.1:8001",
  1072      }
  1073
  1074   Expect this to fail with an exception with a message referencing an incorrect or unexpected host. This message will be language dependent.
  1075   In Python, this message is "certificate verify failed: IP address mismatch, certificate is not valid for '127.0.0.1'". In Go, this message
  1076   is "cannot validate certificate for 127.0.0.1 because it doesn't contain any IP SANs".

View as plain text