Skip to content

PHPLIB-1176: Various improvements for In-Use Encryption tutorial #1122

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Jul 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions docs/examples/create_data_key.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<?php

use MongoDB\BSON\Binary;
use MongoDB\Client;
use MongoDB\Driver\ClientEncryption;

require __DIR__ . '/../../vendor/autoload.php';

$uri = getenv('MONGODB_URI') ?: 'mongodb://127.0.0.1/';

// Generate a secure local key to use for this script
$localKey = new Binary(random_bytes(96));

// Create a client with no encryption options
$client = new Client($uri);

/* Prepare the database for this script. Drop the key vault collection and
* ensure it has a unique index for keyAltNames. This would typically be done
* during application deployment. */
$client->selectCollection('encryption', '__keyVault')->drop();
$client->selectCollection('encryption', '__keyVault')->createIndex(['keyAltNames' => 1], [
'unique' => true,
'partialFilterExpression' => ['keyAltNames' => ['$exists' => true]],
]);

// Create a ClientEncryption object to manage data encryption keys
$clientEncryption = $client->createClientEncryption([
'keyVaultNamespace' => 'encryption.__keyVault',
'kmsProviders' => [
'local' => ['key' => $localKey],
],
]);

/* Create a data encryption key. To store the key ID for later use, you can use
* serialize(), var_export(), etc. */
$keyId = $clientEncryption->createDataKey('local');

print_r($keyId);

// Encrypt a value using the key that was just created
$encryptedValue = $clientEncryption->encrypt('mySecret', [
'algorithm' => ClientEncryption::AEAD_AES_256_CBC_HMAC_SHA_512_DETERMINISTIC,
'keyId' => $keyId,
]);

print_r($encryptedValue);
87 changes: 87 additions & 0 deletions docs/examples/csfle-automatic_encryption-local_schema.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
<?php

use MongoDB\BSON\Binary;
use MongoDB\Client;
use MongoDB\Driver\ClientEncryption;
use MongoDB\Driver\Exception\ServerException;

require __DIR__ . '/../../vendor/autoload.php';

$uri = getenv('MONGODB_URI') ?: 'mongodb://127.0.0.1/';

/* Note: this script assumes that the test database is empty and that the key
* vault collection exists and has a partial, unique index on keyAltNames (as
* demonstrated in the encryption key management scripts). */
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This note is included in scripts other than create_data_key.php and key_alt_name.php, which demonstrate creation of the unique index on the key vault. I'm reasonably happy with this, as it avoids repetition, and also goes along with the note I added in the RST prose above the key_alt_name.php example.

This means all drop() calls are removed and createCollection() only exists in the QE example scripts, since it's necessary to handle encryptedFields. I've revised the comments to remind users about encryptedFields when dropping collections.

ExamplesTest was also updated to ensure a clean test environment going into each script, and create the necessary key vault index.


// Generate a secure local key to use for this script
$localKey = new Binary(random_bytes(96));

// Create a client with no encryption options
$client = new Client($uri);

// Create a ClientEncryption object to manage data encryption keys
$clientEncryption = $client->createClientEncryption([
'keyVaultNamespace' => 'encryption.__keyVault',
'kmsProviders' => [
'local' => ['key' => $localKey],
],
]);

/* Create a data encryption key. Alternatively, this key ID could be read from a
* configuration file. */
$keyId = $clientEncryption->createDataKey('local');

/* Define a JSON schema for the encrypted collection. Since this only utilizes
* encryption schema syntax, it can be used for both the server-side and local
* schema. */
$schema = [
'bsonType' => 'object',
'properties' => [
'encryptedField' => [
'encrypt' => [
'keyId' => [$keyId],
'bsonType' => 'string',
'algorithm' => ClientEncryption::AEAD_AES_256_CBC_HMAC_SHA_512_DETERMINISTIC,
],
],
],
];

/* Create another client with automatic encryption enabled. Configure a local
* schema for the encrypted collection using the "schemaMap" option. */
$encryptedClient = new Client($uri, [], [
'autoEncryption' => [
'keyVaultNamespace' => 'encryption.__keyVault',
'kmsProviders' => ['local' => ['key' => $localKey]],
'schemaMap' => ['test.coll' => $schema],
],
]);

/* Create a new collection for this script. Configure a server-side schema by
* explicitly creating the collection with a "validator" option.
*
* Note: without a server-side schema, another client could potentially insert
* unencrypted data into the collection. Therefore, a local schema should always
* be used in conjunction with a server-side schema. */
$encryptedClient->selectDatabase('test')->createCollection('coll', ['validator' => ['$jsonSchema' => $schema]]);
$encryptedCollection = $encryptedClient->selectCollection('test', 'coll');

/* Using the encrypted client, insert and find a document to demonstrate that
* the encrypted field is automatically encrypted and decrypted. */
$encryptedCollection->insertOne(['_id' => 1, 'encryptedField' => 'mySecret']);

print_r($encryptedCollection->findOne(['_id' => 1]));

/* Using the client configured without encryption, find the same document and
* observe that the field is not automatically decrypted. */
$unencryptedCollection = $client->selectCollection('test', 'coll');

print_r($unencryptedCollection->findOne(['_id' => 1]));

/* Attempt to insert another document with an unencrypted field value to
* demonstrate that the server-side schema is enforced. */
try {
$unencryptedCollection->insertOne(['_id' => 2, 'encryptedField' => 'myOtherSecret']);
} catch (ServerException $e) {
printf("Error inserting document: %s\n", $e->getMessage());
}
79 changes: 79 additions & 0 deletions docs/examples/csfle-automatic_encryption-server_side_schema.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
<?php

use MongoDB\BSON\Binary;
use MongoDB\Client;
use MongoDB\Driver\ClientEncryption;
use MongoDB\Driver\Exception\ServerException;

require __DIR__ . '/../../vendor/autoload.php';

$uri = getenv('MONGODB_URI') ?: 'mongodb://127.0.0.1/';

/* Note: this script assumes that the test database is empty and that the key
* vault collection exists and has a partial, unique index on keyAltNames (as
* demonstrated in the encryption key management scripts). */

// Generate a secure local key to use for this script
$localKey = new Binary(random_bytes(96));

// Create a client with no encryption options
$client = new Client($uri);

// Create a ClientEncryption object to manage data encryption keys
$clientEncryption = $client->createClientEncryption([
'keyVaultNamespace' => 'encryption.__keyVault',
'kmsProviders' => [
'local' => ['key' => $localKey],
],
]);

/* Create a data encryption key. Alternatively, this key ID could be read from a
* configuration file. */
$keyId = $clientEncryption->createDataKey('local');

// Create another client with automatic encryption enabled
$encryptedClient = new Client($uri, [], [
'autoEncryption' => [
'keyVaultNamespace' => 'encryption.__keyVault',
'kmsProviders' => ['local' => ['key' => $localKey]],
],
]);

// Define a JSON schema for the encrypted collection
$schema = [
'bsonType' => 'object',
'properties' => [
'encryptedField' => [
'encrypt' => [
'keyId' => [$keyId],
'bsonType' => 'string',
'algorithm' => ClientEncryption::AEAD_AES_256_CBC_HMAC_SHA_512_DETERMINISTIC,
],
],
],
];

/* Create a new collection for this script. Configure a server-side schema by
* explicitly creating the collection with a "validator" option. */
$encryptedClient->selectDatabase('test')->createCollection('coll', ['validator' => ['$jsonSchema' => $schema]]);
$encryptedCollection = $encryptedClient->selectCollection('test', 'coll');

/* Using the encrypted client, insert and find a document to demonstrate that
* the encrypted field is automatically encrypted and decrypted. */
$encryptedCollection->insertOne(['_id' => 1, 'encryptedField' => 'mySecret']);

print_r($encryptedCollection->findOne(['_id' => 1]));

/* Using the client configured without encryption, find the same document and
* observe that the field is not automatically decrypted. */
$unencryptedCollection = $client->selectCollection('test', 'coll');

print_r($unencryptedCollection->findOne(['_id' => 1]));

/* Attempt to insert another document with an unencrypted field value to
* demonstrate that the server-side schema is enforced. */
try {
$unencryptedCollection->insertOne(['_id' => 2, 'encryptedField' => 'myOtherSecret']);
} catch (ServerException $e) {
printf("Error inserting document: %s\n", $e->getMessage());
}
49 changes: 49 additions & 0 deletions docs/examples/csfle-explicit_encryption.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<?php

use MongoDB\BSON\Binary;
use MongoDB\Client;
use MongoDB\Driver\ClientEncryption;

require __DIR__ . '/../../vendor/autoload.php';

$uri = getenv('MONGODB_URI') ?: 'mongodb://127.0.0.1/';

/* Note: this script assumes that the test database is empty and that the key
* vault collection exists and has a partial, unique index on keyAltNames (as
* demonstrated in the encryption key management scripts). */

// Generate a secure local key to use for this script
$localKey = new Binary(random_bytes(96));

// Create a client with no encryption options
$client = new Client($uri);

// Create a ClientEncryption object to manage data encryption keys
$clientEncryption = $client->createClientEncryption([
'keyVaultNamespace' => 'encryption.__keyVault',
'kmsProviders' => [
'local' => ['key' => $localKey],
],
]);

/* Create a data encryption key. Alternatively, this key ID could be read from a
* configuration file. */
$keyId = $clientEncryption->createDataKey('local');

// Insert a document with a manually encrypted field
$encryptedValue = $clientEncryption->encrypt('mySecret', [
'algorithm' => ClientEncryption::AEAD_AES_256_CBC_HMAC_SHA_512_DETERMINISTIC,
'keyId' => $keyId,
]);

$collection = $client->selectCollection('test', 'coll');
$collection->insertOne(['_id' => 1, 'encryptedField' => $encryptedValue]);

/* Using the client configured without encryption, find the document and observe
* that the field is not automatically decrypted. */
$document = $collection->findOne();

print_r($document);

// Manually decrypt the field
printf("Decrypted: %s\n", $clientEncryption->decrypt($document->encryptedField));
52 changes: 52 additions & 0 deletions docs/examples/csfle-explicit_encryption_automatic_decryption.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<?php

use MongoDB\BSON\Binary;
use MongoDB\Client;
use MongoDB\Driver\ClientEncryption;

require __DIR__ . '/../../vendor/autoload.php';

$uri = getenv('MONGODB_URI') ?: 'mongodb://127.0.0.1/';

/* Note: this script assumes that the test database is empty and that the key
* vault collection exists and has a partial, unique index on keyAltNames (as
* demonstrated in the encryption key management scripts). */

// Generate a secure local key to use for this script
$localKey = new Binary(random_bytes(96));

// Create a client with automatic encryption disabled
$client = new Client($uri, [], [
'autoEncryption' => [
'keyVaultNamespace' => 'encryption.__keyVault',
'kmsProviders' => ['local' => ['key' => $localKey]],
'bypassAutoEncryption' => true,
],
]);

// Create a ClientEncryption object to manage data encryption keys
$clientEncryption = $client->createClientEncryption([
'keyVaultNamespace' => 'encryption.__keyVault',
'kmsProviders' => [
'local' => ['key' => $localKey],
],
]);

/* Create a data encryption key. Alternatively, this key ID could be read from a
* configuration file. */
$keyId = $clientEncryption->createDataKey('local');

// Insert a document with a manually encrypted field
$encryptedValue = $clientEncryption->encrypt('mySecret', [
'algorithm' => ClientEncryption::AEAD_AES_256_CBC_HMAC_SHA_512_DETERMINISTIC,
'keyId' => $keyId,
]);

$collection = $client->selectCollection('test', 'coll');
$collection->insertOne(['_id' => 1, 'encryptedField' => $encryptedValue]);

/* Using the client configured with encryption (but not automatic encryption),
* find the document and observe that the field is automatically decrypted. */
$document = $collection->findOne();

print_r($document);
52 changes: 52 additions & 0 deletions docs/examples/key_alt_name.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<?php

use MongoDB\BSON\Binary;
use MongoDB\Client;
use MongoDB\Driver\ClientEncryption;
use MongoDB\Driver\Exception\ServerException;

require __DIR__ . '/../../vendor/autoload.php';

$uri = getenv('MONGODB_URI') ?: 'mongodb://127.0.0.1/';

// Generate a secure local key to use for this script
$localKey = new Binary(random_bytes(96));

// Create a client with no encryption options
$client = new Client($uri);

/* Prepare the database for this script. Drop the key vault collection and
* ensure it has a unique index for keyAltNames. This would typically be done
* during application deployment. */
$client->selectCollection('encryption', '__keyVault')->drop();
$client->selectCollection('encryption', '__keyVault')->createIndex(['keyAltNames' => 1], [
'unique' => true,
'partialFilterExpression' => ['keyAltNames' => ['$exists' => true]],
]);

// Create a ClientEncryption object to manage data encryption keys
$clientEncryption = $client->createClientEncryption([
'keyVaultNamespace' => 'encryption.__keyVault',
'kmsProviders' => [
'local' => ['key' => $localKey],
],
]);

// Create a data encryption key with an alternate name
$clientEncryption->createDataKey('local', ['keyAltNames' => ['myDataKey']]);

/* Attempt to create a second key with the same name to demonstrate that the
* unique index is enforced. */
try {
$clientEncryption->createDataKey('local', ['keyAltNames' => ['myDataKey']]);
} catch (ServerException $e) {
printf("Error creating key: %s\n", $e->getMessage());
}

// Encrypt a value, using the "keyAltName" option instead of "keyId"
$encryptedValue = $clientEncryption->encrypt('mySecret', [
'algorithm' => ClientEncryption::AEAD_AES_256_CBC_HMAC_SHA_512_DETERMINISTIC,
'keyAltName' => 'myDataKey',
]);

print_r($encryptedValue);
Loading