From 4d209ce7aa92ac543f6b106d94a65508858ccf51 Mon Sep 17 00:00:00 2001 From: Bailey Pearson Date: Wed, 12 Jun 2024 08:14:07 -0600 Subject: [PATCH] feat(NODE-5801): allow multiple providers providers per type (#4137) --- .evergreen/config.in.yml | 8 + .evergreen/config.yml | 8 + .gitignore | 6 + src/client-side-encryption/providers/index.ts | 212 +-- src/client-side-encryption/state_machine.ts | 6 +- src/index.ts | 7 +- .../client_side_encryption.prose.test.js | 232 ++- .../client_side_encryption.spec.test.ts | 21 +- .../tests/legacy/namedKMS.json | 197 +++ .../tests/legacy/namedKMS.yml | 56 + .../tests/unified/namedKMS-createDataKey.json | 396 +++++ .../tests/unified/namedKMS-createDataKey.yml | 175 +++ .../tests/unified/namedKMS-explicit.json | 130 ++ .../tests/unified/namedKMS-explicit.yml | 72 + .../unified/namedKMS-rewrapManyDataKey.json | 1385 +++++++++++++++++ .../unified/namedKMS-rewrapManyDataKey.yml | 432 +++++ test/tools/runner/config.ts | 7 +- .../filters/client_encryption_filter.ts | 4 +- test/tools/spec-runner/index.js | 10 + test/tools/unified-spec-runner/operations.ts | 14 + test/tools/unified-spec-runner/schema.ts | 55 +- .../unified-spec-runner/unified-utils.ts | 212 ++- test/types/client-side-encryption.test-d.ts | 2 +- 23 files changed, 3495 insertions(+), 152 deletions(-) create mode 100644 test/spec/client-side-encryption/tests/legacy/namedKMS.json create mode 100644 test/spec/client-side-encryption/tests/legacy/namedKMS.yml create mode 100644 test/spec/client-side-encryption/tests/unified/namedKMS-createDataKey.json create mode 100644 test/spec/client-side-encryption/tests/unified/namedKMS-createDataKey.yml create mode 100644 test/spec/client-side-encryption/tests/unified/namedKMS-explicit.json create mode 100644 test/spec/client-side-encryption/tests/unified/namedKMS-explicit.yml create mode 100644 test/spec/client-side-encryption/tests/unified/namedKMS-rewrapManyDataKey.json create mode 100644 test/spec/client-side-encryption/tests/unified/namedKMS-rewrapManyDataKey.yml diff --git a/.evergreen/config.in.yml b/.evergreen/config.in.yml index eab1c6fbde..2845e14065 100644 --- a/.evergreen/config.in.yml +++ b/.evergreen/config.in.yml @@ -135,6 +135,8 @@ functions: export CLIENT_ENCRYPTION=${CLIENT_ENCRYPTION} export RUN_WITH_MONGOCRYPTD=${RUN_WITH_MONGOCRYPTD} export CSFLE_KMS_PROVIDERS='${CSFLE_KMS_PROVIDERS}' + export FLE_AWS_KEY2='${FLE_AWS_KEY2}' + export FLE_AWS_SECRET2='${FLE_AWS_SECRET2}' export AWS_ACCESS_KEY_ID='${AWS_ACCESS_KEY_ID}' export AWS_SECRET_ACCESS_KEY='${AWS_SECRET_ACCESS_KEY}' export AWS_REGION='${AWS_REGION}' @@ -193,6 +195,8 @@ functions: export CSFLE_KMS_PROVIDERS='${CSFLE_KMS_PROVIDERS}' export AWS_ACCESS_KEY_ID='${AWS_ACCESS_KEY_ID}' export AWS_SECRET_ACCESS_KEY='${AWS_SECRET_ACCESS_KEY}' + export FLE_AWS_KEY2='${FLE_AWS_KEY2}' + export FLE_AWS_SECRET2='${FLE_AWS_SECRET2}' export AWS_REGION='${AWS_REGION}' export AWS_CMK_ID='${AWS_CMK_ID}' export AWS_DEFAULT_REGION='us-east-1' @@ -471,6 +475,8 @@ functions: cat < prepare_client_encryption.sh export CLIENT_ENCRYPTION='${CLIENT_ENCRYPTION}' export CSFLE_KMS_PROVIDERS='${CSFLE_KMS_PROVIDERS}' + export FLE_AWS_KEY2='${FLE_AWS_KEY2}' + export FLE_AWS_SECRET2='${FLE_AWS_SECRET2}' export AWS_REGION='${AWS_REGION}' export AWS_CMK_ID='${AWS_CMK_ID}' export AWS_ACCESS_KEY_ID='${AWS_ACCESS_KEY_ID}' @@ -908,6 +914,8 @@ functions: cat < prepare_client_encryption.sh export CLIENT_ENCRYPTION='${CLIENT_ENCRYPTION}' export CSFLE_KMS_PROVIDERS='${CSFLE_KMS_PROVIDERS}' + export FLE_AWS_KEY2='${FLE_AWS_KEY2}' + export FLE_AWS_SECRET2='${FLE_AWS_SECRET2}' export AWS_ACCESS_KEY_ID='${AWS_ACCESS_KEY_ID}' export AWS_SECRET_ACCESS_KEY='${AWS_SECRET_ACCESS_KEY}' export AWS_REGION='${AWS_REGION}' diff --git a/.evergreen/config.yml b/.evergreen/config.yml index e60beb8d5b..d80f3d3f4c 100644 --- a/.evergreen/config.yml +++ b/.evergreen/config.yml @@ -108,6 +108,8 @@ functions: export CLIENT_ENCRYPTION=${CLIENT_ENCRYPTION} export RUN_WITH_MONGOCRYPTD=${RUN_WITH_MONGOCRYPTD} export CSFLE_KMS_PROVIDERS='${CSFLE_KMS_PROVIDERS}' + export FLE_AWS_KEY2='${FLE_AWS_KEY2}' + export FLE_AWS_SECRET2='${FLE_AWS_SECRET2}' export AWS_ACCESS_KEY_ID='${AWS_ACCESS_KEY_ID}' export AWS_SECRET_ACCESS_KEY='${AWS_SECRET_ACCESS_KEY}' export AWS_REGION='${AWS_REGION}' @@ -165,6 +167,8 @@ functions: export CSFLE_KMS_PROVIDERS='${CSFLE_KMS_PROVIDERS}' export AWS_ACCESS_KEY_ID='${AWS_ACCESS_KEY_ID}' export AWS_SECRET_ACCESS_KEY='${AWS_SECRET_ACCESS_KEY}' + export FLE_AWS_KEY2='${FLE_AWS_KEY2}' + export FLE_AWS_SECRET2='${FLE_AWS_SECRET2}' export AWS_REGION='${AWS_REGION}' export AWS_CMK_ID='${AWS_CMK_ID}' export AWS_DEFAULT_REGION='us-east-1' @@ -425,6 +429,8 @@ functions: cat < prepare_client_encryption.sh export CLIENT_ENCRYPTION='${CLIENT_ENCRYPTION}' export CSFLE_KMS_PROVIDERS='${CSFLE_KMS_PROVIDERS}' + export FLE_AWS_KEY2='${FLE_AWS_KEY2}' + export FLE_AWS_SECRET2='${FLE_AWS_SECRET2}' export AWS_REGION='${AWS_REGION}' export AWS_CMK_ID='${AWS_CMK_ID}' export AWS_ACCESS_KEY_ID='${AWS_ACCESS_KEY_ID}' @@ -881,6 +887,8 @@ functions: cat < prepare_client_encryption.sh export CLIENT_ENCRYPTION='${CLIENT_ENCRYPTION}' export CSFLE_KMS_PROVIDERS='${CSFLE_KMS_PROVIDERS}' + export FLE_AWS_KEY2='${FLE_AWS_KEY2}' + export FLE_AWS_SECRET2='${FLE_AWS_SECRET2}' export AWS_ACCESS_KEY_ID='${AWS_ACCESS_KEY_ID}' export AWS_SECRET_ACCESS_KEY='${AWS_SECRET_ACCESS_KEY}' export AWS_REGION='${AWS_REGION}' diff --git a/.gitignore b/.gitignore index 636851962a..cb5e9e4ec8 100644 --- a/.gitignore +++ b/.gitignore @@ -96,3 +96,9 @@ node-artifacts # AWS SAM generated test/lambda/.aws-sam test/lambda/env.json + +# files generated by tooling in drivers-evergreen-tools +secrets-export.sh +mo-expansion.sh +mo-expansion.yml +expansions.sh diff --git a/src/client-side-encryption/providers/index.ts b/src/client-side-encryption/providers/index.ts index fb83e7b912..b92cbbcc5f 100644 --- a/src/client-side-encryption/providers/index.ts +++ b/src/client-side-encryption/providers/index.ts @@ -4,124 +4,152 @@ import { loadGCPCredentials } from './gcp'; /** * @public + * + * A data key provider. Allowed values: + * + * - aws, gcp, local, kmip or azure + * - (`mongodb-client-encryption>=6.0.1` only) a named key, in the form of: + * `aws:`, `gcp:`, `local:`, `kmip:`, `azure:` + * where `name` is an alphanumeric string, underscores allowed. */ -export type ClientEncryptionDataKeyProvider = 'aws' | 'azure' | 'gcp' | 'local' | 'kmip'; +export type ClientEncryptionDataKeyProvider = string; + +/** @public */ +export interface AWSKMSProviderConfiguration { + /** + * The access key used for the AWS KMS provider + */ + accessKeyId: string; + + /** + * The secret access key used for the AWS KMS provider + */ + secretAccessKey: string; + + /** + * An optional AWS session token that will be used as the + * X-Amz-Security-Token header for AWS requests. + */ + sessionToken?: string; +} + +/** @public */ +export interface LocalKMSProviderConfiguration { + /** + * The master key used to encrypt/decrypt data keys. + * A 96-byte long Buffer or base64 encoded string. + */ + key: Buffer | string; +} + +/** @public */ +export interface KMIPKMSProviderConfiguration { + /** + * The output endpoint string. + * The endpoint consists of a hostname and port separated by a colon. + * E.g. "example.com:123". A port is always present. + */ + endpoint?: string; +} + +/** @public */ +export type AzureKMSProviderConfiguration = + | { + /** + * The tenant ID identifies the organization for the account + */ + tenantId: string; + + /** + * The client ID to authenticate a registered application + */ + clientId: string; + + /** + * The client secret to authenticate a registered application + */ + clientSecret: string; + + /** + * If present, a host with optional port. E.g. "example.com" or "example.com:443". + * This is optional, and only needed if customer is using a non-commercial Azure instance + * (e.g. a government or China account, which use different URLs). + * Defaults to "login.microsoftonline.com" + */ + identityPlatformEndpoint?: string | undefined; + } + | { + /** + * If present, an access token to authenticate with Azure. + */ + accessToken: string; + }; + +/** @public */ +export type GCPKMSProviderConfiguration = + | { + /** + * The service account email to authenticate + */ + email: string; + + /** + * A PKCS#8 encrypted key. This can either be a base64 string or a binary representation + */ + privateKey: string | Buffer; + + /** + * If present, a host with optional port. E.g. "example.com" or "example.com:443". + * Defaults to "oauth2.googleapis.com" + */ + endpoint?: string | undefined; + } + | { + /** + * If present, an access token to authenticate with GCP. + */ + accessToken: string; + }; /** * @public * Configuration options that are used by specific KMS providers during key generation, encryption, and decryption. + * + * Named KMS providers _are not supported_ for automatic KMS credential fetching. */ export interface KMSProviders { /** * Configuration options for using 'aws' as your KMS provider */ - aws?: - | { - /** - * The access key used for the AWS KMS provider - */ - accessKeyId: string; - - /** - * The secret access key used for the AWS KMS provider - */ - secretAccessKey: string; - - /** - * An optional AWS session token that will be used as the - * X-Amz-Security-Token header for AWS requests. - */ - sessionToken?: string; - } - | Record; + aws?: AWSKMSProviderConfiguration | Record; /** * Configuration options for using 'local' as your KMS provider */ - local?: { - /** - * The master key used to encrypt/decrypt data keys. - * A 96-byte long Buffer or base64 encoded string. - */ - key: Buffer | string; - }; + local?: LocalKMSProviderConfiguration; /** * Configuration options for using 'kmip' as your KMS provider */ - kmip?: { - /** - * The output endpoint string. - * The endpoint consists of a hostname and port separated by a colon. - * E.g. "example.com:123". A port is always present. - */ - endpoint?: string; - }; + kmip?: KMIPKMSProviderConfiguration; /** * Configuration options for using 'azure' as your KMS provider */ - azure?: - | { - /** - * The tenant ID identifies the organization for the account - */ - tenantId: string; - - /** - * The client ID to authenticate a registered application - */ - clientId: string; - - /** - * The client secret to authenticate a registered application - */ - clientSecret: string; - - /** - * If present, a host with optional port. E.g. "example.com" or "example.com:443". - * This is optional, and only needed if customer is using a non-commercial Azure instance - * (e.g. a government or China account, which use different URLs). - * Defaults to "login.microsoftonline.com" - */ - identityPlatformEndpoint?: string | undefined; - } - | { - /** - * If present, an access token to authenticate with Azure. - */ - accessToken: string; - } - | Record; + azure?: AzureKMSProviderConfiguration | Record; /** * Configuration options for using 'gcp' as your KMS provider */ - gcp?: - | { - /** - * The service account email to authenticate - */ - email: string; - - /** - * A PKCS#8 encrypted key. This can either be a base64 string or a binary representation - */ - privateKey: string | Buffer; - - /** - * If present, a host with optional port. E.g. "example.com" or "example.com:443". - * Defaults to "oauth2.googleapis.com" - */ - endpoint?: string | undefined; - } - | { - /** - * If present, an access token to authenticate with GCP. - */ - accessToken: string; - } - | Record; + gcp?: GCPKMSProviderConfiguration | Record; + + [key: string]: + | AWSKMSProviderConfiguration + | LocalKMSProviderConfiguration + | KMIPKMSProviderConfiguration + | AzureKMSProviderConfiguration + | GCPKMSProviderConfiguration + | undefined; } /** diff --git a/src/client-side-encryption/state_machine.ts b/src/client-side-encryption/state_machine.ts index a4b2379fb5..ecdf49d513 100644 --- a/src/client-side-encryption/state_machine.ts +++ b/src/client-side-encryption/state_machine.ts @@ -17,7 +17,7 @@ import { BufferPool, MongoDBCollectionNamespace, promiseWithResolvers } from '.. import { type DataKey } from './client_encryption'; import { MongoCryptError } from './errors'; import { type MongocryptdManager } from './mongocryptd_manager'; -import { type ClientEncryptionDataKeyProvider, type KMSProviders } from './providers'; +import { type KMSProviders } from './providers'; let socks: SocksLib | null = null; function loadSocks(): SocksLib { @@ -110,6 +110,8 @@ export type CSFLEKMSTlsOptions = { kmip?: ClientEncryptionTlsOptions; local?: ClientEncryptionTlsOptions; azure?: ClientEncryptionTlsOptions; + + [key: string]: ClientEncryptionTlsOptions | undefined; }; /** @@ -319,7 +321,7 @@ export class StateMachine { const tlsOptions = this.options.tlsOptions; if (tlsOptions) { - const kmsProvider = request.kmsProvider as ClientEncryptionDataKeyProvider; + const kmsProvider = request.kmsProvider; const providerTlsOptions = tlsOptions[kmsProvider]; if (providerTlsOptions) { const error = this.validateTlsOptions(kmsProvider, providerTlsOptions); diff --git a/src/index.ts b/src/index.ts index daeae592d2..4ba612a7a0 100644 --- a/src/index.ts +++ b/src/index.ts @@ -238,8 +238,13 @@ export { } from './client-side-encryption/errors'; export type { MongocryptdManager } from './client-side-encryption/mongocryptd_manager'; export type { + AWSKMSProviderConfiguration, + AzureKMSProviderConfiguration, ClientEncryptionDataKeyProvider, - KMSProviders + GCPKMSProviderConfiguration, + KMIPKMSProviderConfiguration, + KMSProviders, + LocalKMSProviderConfiguration } from './client-side-encryption/providers/index'; export type { ClientEncryptionTlsOptions, diff --git a/test/integration/client-side-encryption/client_side_encryption.prose.test.js b/test/integration/client-side-encryption/client_side_encryption.prose.test.js index 73530f4190..6b0b7742ac 100644 --- a/test/integration/client-side-encryption/client_side_encryption.prose.test.js +++ b/test/integration/client-side-encryption/client_side_encryption.prose.test.js @@ -7,7 +7,7 @@ const path = require('path'); const { dropCollection, APMEventCollector } = require('../shared'); const { EJSON } = BSON; -const { LEGACY_HELLO_COMMAND } = require('../../mongodb'); +const { LEGACY_HELLO_COMMAND, MongoCryptError } = require('../../mongodb'); const { MongoServerError, MongoServerSelectionError, MongoClient } = require('../../mongodb'); const { getEncryptExtraOptions } = require('../../tools/utils'); const { installNodeDNSWorkaroundHooks } = require('../../tools/runner/hooks/configuration'); @@ -18,6 +18,9 @@ const { } = require('../../spec/client-side-encryption/external/external-schema.json'); /* eslint-disable no-restricted-modules */ const { ClientEncryption } = require('../../../src/client-side-encryption/client_encryption'); +const { + ClientSideEncryptionFilter +} = require('../../tools/runner/filters/client_encryption_filter'); const getKmsProviders = (localKey, kmipEndpoint, azureEndpoint, gcpEndpoint) => { const result = BSON.EJSON.parse(process.env.CSFLE_KMS_PROVIDERS || '{}'); @@ -1366,6 +1369,8 @@ describe('Client Side Encryption Prose Tests', metadata, function () { let clientEncryptionWithTls; let clientEncryptionWithTlsExpired; let clientEncryptionWithInvalidHostname; + let clientEncryptionWithNames; + let keyvaultClient; beforeEach(async function () { const tlsCaOptions = { @@ -1466,8 +1471,13 @@ describe('Client Side Encryption Prose Tests', metadata, function () { }); await clientWithInvalidHostname.connect(); break; + case 'Named AWS': + case 'Named Azure': + case 'Named GCP': + case 'Named KMIP': + break; default: - throw new Error('unexpected test case'); + throw new Error('unexpected test case: ' + this.currentTest.title); } }); @@ -1476,7 +1486,8 @@ describe('Client Side Encryption Prose Tests', metadata, function () { clientNoTls, clientWithTls, clientWithTlsExpired, - clientWithInvalidHostname + clientWithInvalidHostname, + keyvaultClient ]; for (const client of allClients) { if (client) { @@ -1647,8 +1658,8 @@ describe('Client Side Encryption Prose Tests', metadata, function () { it('should fail with no TLS', metadata, async function () { if (gte(coerce(process.version), coerce('19'))) { - this.skip('TODO(NODE-4942): fix failing csfle kmip test on Node19+'); - return; + this.test.skipReason = 'TODO(NODE-4942): fix failing csfle kmip test on Node19+'; + this.skip(); } try { await clientEncryptionNoTls.createDataKey('kmip', { masterKey }); @@ -1685,6 +1696,217 @@ describe('Client Side Encryption Prose Tests', metadata, function () { } }); }); + + context( + 'Case 5: `tlsDisableOCSPEndpointCheck` is permitted', + metadata, + function () {} + ).skipReason = 'TODO(NODE-4840): Node does not support any OCSP options'; + + context('Case 6: named KMS providers apply TLS options', function () { + afterEach(() => keyvaultClient?.close()); + beforeEach(async function () { + const filter = new ClientSideEncryptionFilter(); + await filter.initializeFilter({}, {}); + const shouldSkip = filter.filter({ + metadata: { + requires: { + // 6.0.1 includes libmongocrypt 1.10. + clientSideEncryption: '>=6.0.1' + } + } + }); + if (typeof shouldSkip === 'string') { + this.currentTest.skipReason = shouldSkip; + this.skip(); + } + const providers = getKmsProviders(); + keyvaultClient = this.configuration.newClient(); + + clientEncryptionWithNames = new ClientEncryption(keyvaultClient, { + kmsProviders: { + 'aws:no_client_cert': { + accessKeyId: providers.aws.accessKeyId, + secretAccessKey: providers.aws.secretAccessKey + }, + 'azure:no_client_cert': { + tenantId: providers.azure.tenantId, + clientId: providers.azure.clientId, + clientSecret: providers.azure.clientId, + identityPlatformEndpoint: '127.0.0.1:8002' + }, + 'gcp:no_client_cert': { + email: providers.gcp.email, + privateKey: providers.gcp.privateKey, + endpoint: '127.0.0.1:8002' + }, + 'kmip:no_client_cert': { + endpoint: '127.0.0.1:5698' + }, + 'aws:with_tls': { + accessKeyId: providers.aws.accessKeyId, + secretAccessKey: providers.aws.secretAccessKey + }, + 'azure:with_tls': { + tenantId: providers.azure.tenantId, + clientId: providers.azure.clientId, + clientSecret: providers.azure.clientId, + identityPlatformEndpoint: '127.0.0.1:8002' + }, + 'gcp:with_tls': { + email: providers.gcp.email, + privateKey: providers.gcp.privateKey, + endpoint: '127.0.0.1:8002' + }, + 'kmip:with_tls': { + endpoint: '127.0.0.1:5698' + } + }, + tlsOptions: { + 'aws:no_client_cert': { + tlsCAFile: process.env.KMIP_TLS_CA_FILE + }, + 'azure:no_client_cert': { + tlsCAFile: process.env.KMIP_TLS_CA_FILE + }, + 'gcp:no_client_cert': { + tlsCAFile: process.env.KMIP_TLS_CA_FILE + }, + 'kmip:no_client_cert': { + tlsCAFile: process.env.KMIP_TLS_CA_FILE + }, + 'aws:with_tls': { + tlsCAFile: process.env.KMIP_TLS_CA_FILE, + tlsCertificateKeyFile: process.env.KMIP_TLS_CERT_FILE + }, + 'azure:with_tls': { + tlsCAFile: process.env.KMIP_TLS_CA_FILE, + tlsCertificateKeyFile: process.env.KMIP_TLS_CERT_FILE + }, + 'gcp:with_tls': { + tlsCAFile: process.env.KMIP_TLS_CA_FILE, + tlsCertificateKeyFile: process.env.KMIP_TLS_CERT_FILE + }, + 'kmip:with_tls': { + tlsCAFile: process.env.KMIP_TLS_CA_FILE, + tlsCertificateKeyFile: process.env.KMIP_TLS_CERT_FILE + } + }, + keyVaultNamespace: 'db.keys' + }); + }); + it('Named AWS', async function () { + { + // Call `client_encryption_with_names.createDataKey()` with "aws:no_client_cert" as the provider and the following masterKey. + const error = await clientEncryptionWithNames + .createDataKey('aws:no_client_cert', { + masterKey: { + region: 'us-east-1', + key: 'arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0', + endpoint: '127.0.0.1:8002' + } + }) + .catch(e => e); + + // Expect an error indicating TLS handshake failed. + expect(error).to.be.instanceof(MongoCryptError); + expect(error.cause).to.match(/tlsv13 alert certificate required/); + } + { + // Call `client_encryption_with_names.createDataKey()` with "aws:with_tls" as the provider and the same masterKey. + const error = await clientEncryptionWithNames + .createDataKey('aws:with_tls', { + masterKey: { + region: 'us-east-1', + key: 'arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0', + endpoint: '127.0.0.1:8002' + } + }) + .catch(e => e); + + // Expect an error from libmongocrypt with a message containing the string: "parse error". This implies TLS handshake succeeded. + expect(error).to.be.instanceof(MongoCryptError); + expect(error).to.match(/parse error/); + } + }); + + it('Named Azure', async function () { + { + // Call `client_encryption_with_names.createDataKey()` with "aws:no_client_cert" as the provider and the following masterKey. + const error = await clientEncryptionWithNames + .createDataKey('azure:no_client_cert', { + masterKey: { keyVaultEndpoint: 'doesnotexist.local', keyName: 'foo' } + }) + .catch(e => e); + + // Expect an error indicating TLS handshake failed. + expect(error).to.be.instanceof(MongoCryptError); + expect(error.cause).to.match(/tlsv13 alert certificate required/); + } + { + // Call `client_encryption_with_names.createDataKey()` with "aws:with_tls" as the provider and the same masterKey. + const error = await clientEncryptionWithNames + .createDataKey('azure:with_tls', { + masterKey: { keyVaultEndpoint: 'doesnotexist.local', keyName: 'foo' } + }) + .catch(e => e); + + // Expect an error from libmongocrypt with a message containing the string: "parse error". This implies TLS handshake succeeded. + expect(error).to.be.instanceof(MongoCryptError); + expect(error).to.match(/HTTP status=404/); + } + }); + + it('Named GCP', async function () { + { + // Call `client_encryption_with_names.createDataKey()` with "aws:no_client_cert" as the provider and the following masterKey. + const error = await clientEncryptionWithNames + .createDataKey('gcp:no_client_cert', { + masterKey: { projectId: 'foo', location: 'bar', keyRing: 'baz', keyName: 'foo' } + }) + .catch(e => e); + + // Expect an error indicating TLS handshake failed. + expect(error).to.be.instanceof(MongoCryptError); + expect(error.cause).to.match(/tlsv13 alert certificate required/); + } + { + // Call `client_encryption_with_names.createDataKey()` with "aws:with_tls" as the provider and the same masterKey. + const error = await clientEncryptionWithNames + .createDataKey('gcp:with_tls', { + masterKey: { projectId: 'foo', location: 'bar', keyRing: 'baz', keyName: 'foo' } + }) + .catch(e => e); + + // Expect an error from libmongocrypt with a message containing the string: "parse error". This implies TLS handshake succeeded. + expect(error).to.be.instanceof(MongoCryptError); + expect(error).to.match(/HTTP status=404/); + } + }); + + it('Named KMIP', async function () { + { + // Call `client_encryption_with_names.createDataKey()` with "aws:no_client_cert" as the provider and the following masterKey. + const error = await clientEncryptionWithNames + .createDataKey('kmip:no_client_cert', { + masterKey: {} + }) + .catch(e => e); + + // Expect an error indicating TLS handshake failed. + expect(error).to.be.instanceof(MongoCryptError); + expect(error.cause).to.match(/handshake|before secure TLS connection was established/); + } + { + // Call `client_encryption_with_names.createDataKey()` with "aws:with_tls" as the provider and the same masterKey. + await clientEncryptionWithNames.createDataKey('kmip:with_tls', { + masterKey: {} + }); + + // Expect success. + } + }); + }); }); context('12. Explicit Encryption', eeMetadata, function () { diff --git a/test/integration/client-side-encryption/client_side_encryption.spec.test.ts b/test/integration/client-side-encryption/client_side_encryption.spec.test.ts index cb8b42ba69..77bcdf69c3 100644 --- a/test/integration/client-side-encryption/client_side_encryption.spec.test.ts +++ b/test/integration/client-side-encryption/client_side_encryption.spec.test.ts @@ -97,7 +97,10 @@ describe('Client Side Encryption (Legacy)', function () { } if ( - description === 'Insert a document with auto encryption using KMIP delegated KMS provider' + [ + 'Insert a document with auto encryption using KMIP delegated KMS provider', + 'Automatically encrypt and decrypt with a named KMS provider' + ].includes(description) ) { if ( typeof filter.filter({ @@ -131,7 +134,21 @@ describe('Client Side Encryption (Unified)', function () { 'rewrap with new Azure KMS provider', 'rewrap with new AWS KMS provider', 'create datakey with KMIP delegated KMS provider', - 'Insert a document with auto encryption using KMIP delegated KMS provider' + 'Insert a document with auto encryption using KMIP delegated KMS provider', + 'create data key with named AWS KMS provider', + 'create datakey with named Azure KMS provider', + 'create datakey with named GCP KMS provider', + 'create datakey with named KMIP KMS provider', + 'create datakey with named local KMS provider', + 'can explicitly decrypt with a named KMS provider', + 'rewrap to aws:name1', + 'rewrap to azure:name1', + 'rewrap to gcp:name1', + 'rewrap to kmip:name1', + 'rewrap to local:name1', + 'rewrap from local:name1 to local:name2', + 'rewrap from aws:name1 to aws:name2', + 'can explicitly encrypt with a named KMS provider' ]; if (delegatedKMIPTests.includes(description)) { const shouldSkip = filter.filter({ diff --git a/test/spec/client-side-encryption/tests/legacy/namedKMS.json b/test/spec/client-side-encryption/tests/legacy/namedKMS.json new file mode 100644 index 0000000000..c859443585 --- /dev/null +++ b/test/spec/client-side-encryption/tests/legacy/namedKMS.json @@ -0,0 +1,197 @@ +{ + "runOn": [ + { + "minServerVersion": "4.1.10" + } + ], + "database_name": "default", + "collection_name": "default", + "data": [], + "json_schema": { + "properties": { + "encrypted_string": { + "encrypt": { + "keyId": [ + { + "$binary": { + "base64": "local+name2+AAAAAAAAAA==", + "subType": "04" + } + } + ], + "bsonType": "string", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + } + }, + "bsonType": "object" + }, + "key_vault_data": [ + { + "_id": { + "$binary": { + "base64": "local+name2+AAAAAAAAAA==", + "subType": "04" + } + }, + "keyMaterial": { + "$binary": { + "base64": "DX3iUuOlBsx6wBX9UZ3v/qXk1HNeBace2J+h/JwsDdF/vmSXLZ1l1VmZYIcpVFy6ODhdbzLjd4pNgg9wcm4etYig62KNkmtZ0/s1tAL5VsuW/s7/3PYnYGznZTFhLjIVcOH/RNoRj2eQb/sRTyivL85wePEpAU/JzuBj6qO9Y5txQgs1k0J3aNy10R9aQ8kC1NuSSpLAIXwE6DlNDDJXhw==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1552949630483" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1552949630483" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "local:name2" + } + } + ], + "tests": [ + { + "description": "Automatically encrypt and decrypt with a named KMS provider", + "clientOptions": { + "autoEncryptOpts": { + "kmsProviders": { + "local:name2": { + "key": { + "$binary": { + "base64": "local+name2+YUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", + "subType": "00" + } + } + } + } + } + }, + "operations": [ + { + "name": "insertOne", + "arguments": { + "document": { + "_id": 1, + "encrypted_string": "string0" + } + } + }, + { + "name": "find", + "arguments": { + "filter": { + "_id": 1 + } + }, + "result": [ + { + "_id": 1, + "encrypted_string": "string0" + } + ] + } + ], + "expectations": [ + { + "command_started_event": { + "command": { + "listCollections": 1, + "filter": { + "name": "default" + } + }, + "command_name": "listCollections" + } + }, + { + "command_started_event": { + "command": { + "find": "datakeys", + "filter": { + "$or": [ + { + "_id": { + "$in": [ + { + "$binary": { + "base64": "local+name2+AAAAAAAAAA==", + "subType": "04" + } + } + ] + } + }, + { + "keyAltNames": { + "$in": [] + } + } + ] + }, + "$db": "keyvault", + "readConcern": { + "level": "majority" + } + }, + "command_name": "find" + } + }, + { + "command_started_event": { + "command": { + "insert": "default", + "documents": [ + { + "_id": 1, + "encrypted_string": { + "$binary": { + "base64": "AZaHGpfp2pntvgAAAAAAAAAC07sFvTQ0I4O2U49hpr4HezaK44Ivluzv5ntQBTYHDlAJMLyRMyB6Dl+UGHBgqhHe/Xw+pcT9XdiUoOJYAx9g+w==", + "subType": "06" + } + } + } + ], + "ordered": true + }, + "command_name": "insert" + } + }, + { + "command_started_event": { + "command": { + "find": "default", + "filter": { + "_id": 1 + } + }, + "command_name": "find" + } + } + ], + "outcome": { + "collection": { + "data": [ + { + "_id": 1, + "encrypted_string": { + "$binary": { + "base64": "AZaHGpfp2pntvgAAAAAAAAAC07sFvTQ0I4O2U49hpr4HezaK44Ivluzv5ntQBTYHDlAJMLyRMyB6Dl+UGHBgqhHe/Xw+pcT9XdiUoOJYAx9g+w==", + "subType": "06" + } + } + } + ] + } + } + } + ] +} diff --git a/test/spec/client-side-encryption/tests/legacy/namedKMS.yml b/test/spec/client-side-encryption/tests/legacy/namedKMS.yml new file mode 100644 index 0000000000..7bd456abaf --- /dev/null +++ b/test/spec/client-side-encryption/tests/legacy/namedKMS.yml @@ -0,0 +1,56 @@ +runOn: + - minServerVersion: "4.1.10" +database_name: &database_name "default" +collection_name: &collection_name "default" + +data: [] +json_schema: {'properties': {'encrypted_string': {'encrypt': {'keyId': [{'$binary': {'base64': 'local+name2+AAAAAAAAAA==', 'subType': '04'}}], 'bsonType': 'string', 'algorithm': 'AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic'}}}, 'bsonType': 'object'} +key_vault_data: [{'_id': {'$binary': {'base64': 'local+name2+AAAAAAAAAA==', 'subType': '04'}}, 'keyMaterial': {'$binary': {'base64': 'DX3iUuOlBsx6wBX9UZ3v/qXk1HNeBace2J+h/JwsDdF/vmSXLZ1l1VmZYIcpVFy6ODhdbzLjd4pNgg9wcm4etYig62KNkmtZ0/s1tAL5VsuW/s7/3PYnYGznZTFhLjIVcOH/RNoRj2eQb/sRTyivL85wePEpAU/JzuBj6qO9Y5txQgs1k0J3aNy10R9aQ8kC1NuSSpLAIXwE6DlNDDJXhw==', 'subType': '00'}}, 'creationDate': {'$date': {'$numberLong': '1552949630483'}}, 'updateDate': {'$date': {'$numberLong': '1552949630483'}}, 'status': {'$numberInt': '0'}, 'masterKey': {'provider': 'local:name2'}}] + +tests: + - description: "Automatically encrypt and decrypt with a named KMS provider" + clientOptions: + autoEncryptOpts: + kmsProviders: + "local:name2": {'key': {'$binary': {'base64': 'local+name2+YUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk', 'subType': '00'}}} + operations: + - name: insertOne + arguments: + document: &doc0 { _id: 1, encrypted_string: "string0" } + - name: find + arguments: + filter: { _id: 1 } + result: [*doc0] + expectations: + # Auto encryption will request the collection info. + - command_started_event: + command: + listCollections: 1 + filter: + name: *collection_name + command_name: listCollections + # Then key is fetched from the key vault. + - command_started_event: + command: + find: datakeys + filter: {"$or": [{"_id": {"$in": [ {'$binary': {'base64': 'local+name2+AAAAAAAAAA==', 'subType': '04'}} ] }}, {"keyAltNames": {"$in": []}}]} + $db: keyvault + readConcern: { level: "majority" } + command_name: find + - command_started_event: + command: + insert: *collection_name + documents: + - &doc0_encrypted { _id: 1, encrypted_string: {'$binary': {'base64': 'AZaHGpfp2pntvgAAAAAAAAAC07sFvTQ0I4O2U49hpr4HezaK44Ivluzv5ntQBTYHDlAJMLyRMyB6Dl+UGHBgqhHe/Xw+pcT9XdiUoOJYAx9g+w==', 'subType': '06'}} } + ordered: true + command_name: insert + - command_started_event: + command: + find: *collection_name + filter: { _id: 1 } + command_name: find + outcome: + collection: + # Outcome is checked using a separate MongoClient without auto encryption. + data: + - *doc0_encrypted \ No newline at end of file diff --git a/test/spec/client-side-encryption/tests/unified/namedKMS-createDataKey.json b/test/spec/client-side-encryption/tests/unified/namedKMS-createDataKey.json new file mode 100644 index 0000000000..4d75e4cf51 --- /dev/null +++ b/test/spec/client-side-encryption/tests/unified/namedKMS-createDataKey.json @@ -0,0 +1,396 @@ +{ + "description": "namedKMS-createDataKey", + "schemaVersion": "1.18", + "runOnRequirements": [ + { + "csfle": true + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "clientEncryption": { + "id": "clientEncryption0", + "clientEncryptionOpts": { + "keyVaultClient": "client0", + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "aws:name1": { + "accessKeyId": { + "$$placeholder": 1 + }, + "secretAccessKey": { + "$$placeholder": 1 + } + }, + "azure:name1": { + "tenantId": { + "$$placeholder": 1 + }, + "clientId": { + "$$placeholder": 1 + }, + "clientSecret": { + "$$placeholder": 1 + } + }, + "gcp:name1": { + "email": { + "$$placeholder": 1 + }, + "privateKey": { + "$$placeholder": 1 + } + }, + "kmip:name1": { + "endpoint": { + "$$placeholder": 1 + } + }, + "local:name1": { + "key": { + "$$placeholder": 1 + } + } + } + } + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "keyvault" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "datakeys" + } + } + ], + "initialData": [ + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [] + } + ], + "tests": [ + { + "description": "create data key with named AWS KMS provider", + "operations": [ + { + "name": "createDataKey", + "object": "clientEncryption0", + "arguments": { + "kmsProvider": "aws:name1", + "opts": { + "masterKey": { + "key": "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0", + "region": "us-east-1" + } + } + }, + "expectResult": { + "$$type": "binData" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "insert": "datakeys", + "documents": [ + { + "_id": { + "$$type": "binData" + }, + "keyMaterial": { + "$$type": "binData" + }, + "creationDate": { + "$$type": "date" + }, + "updateDate": { + "$$type": "date" + }, + "status": { + "$$exists": true + }, + "masterKey": { + "provider": "aws:name1", + "key": "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0", + "region": "us-east-1" + } + } + ], + "writeConcern": { + "w": "majority" + } + } + } + } + ] + } + ] + }, + { + "description": "create datakey with named Azure KMS provider", + "operations": [ + { + "name": "createDataKey", + "object": "clientEncryption0", + "arguments": { + "kmsProvider": "azure:name1", + "opts": { + "masterKey": { + "keyVaultEndpoint": "key-vault-csfle.vault.azure.net", + "keyName": "key-name-csfle" + } + } + }, + "expectResult": { + "$$type": "binData" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "insert": "datakeys", + "documents": [ + { + "_id": { + "$$type": "binData" + }, + "keyMaterial": { + "$$type": "binData" + }, + "creationDate": { + "$$type": "date" + }, + "updateDate": { + "$$type": "date" + }, + "status": { + "$$exists": true + }, + "masterKey": { + "provider": "azure:name1", + "keyVaultEndpoint": "key-vault-csfle.vault.azure.net", + "keyName": "key-name-csfle" + } + } + ], + "writeConcern": { + "w": "majority" + } + } + } + } + ] + } + ] + }, + { + "description": "create datakey with named GCP KMS provider", + "operations": [ + { + "name": "createDataKey", + "object": "clientEncryption0", + "arguments": { + "kmsProvider": "gcp:name1", + "opts": { + "masterKey": { + "projectId": "devprod-drivers", + "location": "global", + "keyRing": "key-ring-csfle", + "keyName": "key-name-csfle" + } + } + }, + "expectResult": { + "$$type": "binData" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "insert": "datakeys", + "documents": [ + { + "_id": { + "$$type": "binData" + }, + "keyMaterial": { + "$$type": "binData" + }, + "creationDate": { + "$$type": "date" + }, + "updateDate": { + "$$type": "date" + }, + "status": { + "$$exists": true + }, + "masterKey": { + "provider": "gcp:name1", + "projectId": "devprod-drivers", + "location": "global", + "keyRing": "key-ring-csfle", + "keyName": "key-name-csfle" + } + } + ], + "writeConcern": { + "w": "majority" + } + } + } + } + ] + } + ] + }, + { + "description": "create datakey with named KMIP KMS provider", + "operations": [ + { + "name": "createDataKey", + "object": "clientEncryption0", + "arguments": { + "kmsProvider": "kmip:name1" + }, + "expectResult": { + "$$type": "binData" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "insert": "datakeys", + "documents": [ + { + "_id": { + "$$type": "binData" + }, + "keyMaterial": { + "$$type": "binData" + }, + "creationDate": { + "$$type": "date" + }, + "updateDate": { + "$$type": "date" + }, + "status": { + "$$exists": true + }, + "masterKey": { + "provider": "kmip:name1", + "keyId": { + "$$type": "string" + } + } + } + ], + "writeConcern": { + "w": "majority" + } + } + } + } + ] + } + ] + }, + { + "description": "create datakey with named local KMS provider", + "operations": [ + { + "name": "createDataKey", + "object": "clientEncryption0", + "arguments": { + "kmsProvider": "local:name1" + }, + "expectResult": { + "$$type": "binData" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "insert": "datakeys", + "documents": [ + { + "_id": { + "$$type": "binData" + }, + "keyMaterial": { + "$$type": "binData" + }, + "creationDate": { + "$$type": "date" + }, + "updateDate": { + "$$type": "date" + }, + "status": { + "$$exists": true + }, + "masterKey": { + "provider": "local:name1" + } + } + ], + "writeConcern": { + "w": "majority" + } + } + } + } + ] + } + ] + } + ] +} diff --git a/test/spec/client-side-encryption/tests/unified/namedKMS-createDataKey.yml b/test/spec/client-side-encryption/tests/unified/namedKMS-createDataKey.yml new file mode 100644 index 0000000000..0e3a53b649 --- /dev/null +++ b/test/spec/client-side-encryption/tests/unified/namedKMS-createDataKey.yml @@ -0,0 +1,175 @@ +description: namedKMS-createDataKey + +schemaVersion: "1.18" + +runOnRequirements: + - csfle: true + +createEntities: + - client: + id: &client0 client0 + observeEvents: + - commandStartedEvent + - clientEncryption: + id: &clientEncryption0 clientEncryption0 + clientEncryptionOpts: + keyVaultClient: *client0 + keyVaultNamespace: keyvault.datakeys + kmsProviders: + "aws:name1": { accessKeyId: { $$placeholder: 1 }, secretAccessKey: { $$placeholder: 1 } } + "azure:name1": { tenantId: { $$placeholder: 1 }, clientId: { $$placeholder: 1 }, clientSecret: { $$placeholder: 1 } } + "gcp:name1": { email: { $$placeholder: 1 }, privateKey: { $$placeholder: 1 } } + "kmip:name1": { endpoint: { $$placeholder: 1 } } + "local:name1": { key: { $$placeholder: 1 } } + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name keyvault + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name datakeys + +initialData: + - databaseName: *database0Name + collectionName: *collection0Name + documents: [] + +tests: + - description: create data key with named AWS KMS provider + operations: + - name: createDataKey + object: *clientEncryption0 + arguments: + kmsProvider: "aws:name1" + opts: + masterKey: &new_aws_masterkey + key: "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0" + region: us-east-1 + expectResult: { $$type: binData } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + databaseName: *database0Name + command: + insert: *collection0Name + documents: + - _id: { $$type: binData } + keyMaterial: { $$type: binData } + creationDate: { $$type: date } + updateDate: { $$type: date } + status: { $$exists: true } + masterKey: + provider: "aws:name1" + <<: *new_aws_masterkey + writeConcern: { w: majority } + + - description: create datakey with named Azure KMS provider + operations: + - name: createDataKey + object: *clientEncryption0 + arguments: + kmsProvider: "azure:name1" + opts: + masterKey: &new_azure_masterkey + keyVaultEndpoint: key-vault-csfle.vault.azure.net + keyName: key-name-csfle + expectResult: { $$type: binData } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + databaseName: *database0Name + command: + insert: *collection0Name + documents: + - _id: { $$type: binData } + keyMaterial: { $$type: binData } + creationDate: { $$type: date } + updateDate: { $$type: date } + status: { $$exists: true } + masterKey: + provider: "azure:name1" + <<: *new_azure_masterkey + writeConcern: { w: majority } + + - description: create datakey with named GCP KMS provider + operations: + - name: createDataKey + object: *clientEncryption0 + arguments: + kmsProvider: "gcp:name1" + opts: + masterKey: &new_gcp_masterkey + projectId: devprod-drivers + location: global + keyRing: key-ring-csfle + keyName: key-name-csfle + expectResult: { $$type: binData } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + databaseName: *database0Name + command: + insert: *collection0Name + documents: + - _id: { $$type: binData } + keyMaterial: { $$type: binData } + creationDate: { $$type: date } + updateDate: { $$type: date } + status: { $$exists: true } + masterKey: + provider: "gcp:name1" + <<: *new_gcp_masterkey + writeConcern: { w: majority } + + - description: create datakey with named KMIP KMS provider + operations: + - name: createDataKey + object: *clientEncryption0 + arguments: + kmsProvider: "kmip:name1" + expectResult: { $$type: binData } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + databaseName: *database0Name + command: + insert: *collection0Name + documents: + - _id: { $$type: binData } + keyMaterial: { $$type: binData } + creationDate: { $$type: date } + updateDate: { $$type: date } + status: { $$exists: true } + masterKey: + provider: "kmip:name1" + keyId: { $$type: string } + writeConcern: { w: majority } + + - description: create datakey with named local KMS provider + operations: + - name: createDataKey + object: *clientEncryption0 + arguments: + kmsProvider: "local:name1" + expectResult: { $$type: binData } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + databaseName: *database0Name + command: + insert: *collection0Name + documents: + - _id: { $$type: binData } + keyMaterial: { $$type: binData } + creationDate: { $$type: date } + updateDate: { $$type: date } + status: { $$exists: true } + masterKey: + provider: "local:name1" + writeConcern: { w: majority } diff --git a/test/spec/client-side-encryption/tests/unified/namedKMS-explicit.json b/test/spec/client-side-encryption/tests/unified/namedKMS-explicit.json new file mode 100644 index 0000000000..e28d7e8b30 --- /dev/null +++ b/test/spec/client-side-encryption/tests/unified/namedKMS-explicit.json @@ -0,0 +1,130 @@ +{ + "description": "namedKMS-explicit", + "schemaVersion": "1.18", + "runOnRequirements": [ + { + "csfle": true + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "clientEncryption": { + "id": "clientEncryption0", + "clientEncryptionOpts": { + "keyVaultClient": "client0", + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "local:name2": { + "key": "local+name2+YUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk" + } + } + } + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "keyvault" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "datakeys" + } + } + ], + "initialData": [ + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "_id": { + "$binary": { + "base64": "local+name2+AAAAAAAAAA==", + "subType": "04" + } + }, + "keyAltNames": [ + "local:name2" + ], + "keyMaterial": { + "$binary": { + "base64": "DX3iUuOlBsx6wBX9UZ3v/qXk1HNeBace2J+h/JwsDdF/vmSXLZ1l1VmZYIcpVFy6ODhdbzLjd4pNgg9wcm4etYig62KNkmtZ0/s1tAL5VsuW/s7/3PYnYGznZTFhLjIVcOH/RNoRj2eQb/sRTyivL85wePEpAU/JzuBj6qO9Y5txQgs1k0J3aNy10R9aQ8kC1NuSSpLAIXwE6DlNDDJXhw==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1552949630483" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1552949630483" + } + }, + "status": { + "$numberInt": "0" + }, + "masterKey": { + "provider": "local:name2" + } + } + ] + } + ], + "tests": [ + { + "description": "can explicitly encrypt with a named KMS provider", + "operations": [ + { + "name": "encrypt", + "object": "clientEncryption0", + "arguments": { + "value": "foobar", + "opts": { + "keyAltName": "local:name2", + "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + } + }, + "expectResult": { + "$binary": { + "base64": "AZaHGpfp2pntvgAAAAAAAAAC4yX2LTAuN253GAkEO2ZXp4GpCyM7yoVNJMQQl+6uzxMs03IprLC7DL2vr18x9LwOimjTS9YbMJhrnFkEPuNhbg==", + "subType": "06" + } + } + } + ] + }, + { + "description": "can explicitly decrypt with a named KMS provider", + "operations": [ + { + "name": "decrypt", + "object": "clientEncryption0", + "arguments": { + "value": { + "$binary": { + "base64": "AZaHGpfp2pntvgAAAAAAAAAC4yX2LTAuN253GAkEO2ZXp4GpCyM7yoVNJMQQl+6uzxMs03IprLC7DL2vr18x9LwOimjTS9YbMJhrnFkEPuNhbg==", + "subType": "06" + } + } + }, + "expectResult": "foobar" + } + ] + } + ] +} diff --git a/test/spec/client-side-encryption/tests/unified/namedKMS-explicit.yml b/test/spec/client-side-encryption/tests/unified/namedKMS-explicit.yml new file mode 100644 index 0000000000..aa9fb3bc87 --- /dev/null +++ b/test/spec/client-side-encryption/tests/unified/namedKMS-explicit.yml @@ -0,0 +1,72 @@ +description: namedKMS-explicit + +schemaVersion: "1.18" + +runOnRequirements: + - csfle: true + +createEntities: + - client: + id: &client0 client0 + observeEvents: + - commandStartedEvent + - clientEncryption: + id: &clientEncryption0 clientEncryption0 + clientEncryptionOpts: + keyVaultClient: *client0 + keyVaultNamespace: keyvault.datakeys + kmsProviders: + "local:name2" : { key: "local+name2+YUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk" } + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name keyvault + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name datakeys + +initialData: + - databaseName: *database0Name + collectionName: *collection0Name + documents: + - { + "_id": { + "$binary": { + "base64": "local+name2+AAAAAAAAAA==", + "subType": "04" + } + }, + "keyAltNames": ["local:name2"], + "keyMaterial": { + "$binary": { + "base64": "DX3iUuOlBsx6wBX9UZ3v/qXk1HNeBace2J+h/JwsDdF/vmSXLZ1l1VmZYIcpVFy6ODhdbzLjd4pNgg9wcm4etYig62KNkmtZ0/s1tAL5VsuW/s7/3PYnYGznZTFhLjIVcOH/RNoRj2eQb/sRTyivL85wePEpAU/JzuBj6qO9Y5txQgs1k0J3aNy10R9aQ8kC1NuSSpLAIXwE6DlNDDJXhw==", + "subType": "00" + } + }, + "creationDate": {"$date": {"$numberLong": "1552949630483"}}, + "updateDate": {"$date": {"$numberLong": "1552949630483"}}, + "status": {"$numberInt": "0"}, + "masterKey": {"provider": "local:name2"} + } + +tests: + - description: can explicitly encrypt with a named KMS provider + operations: + - name: encrypt + object: *clientEncryption0 + arguments: + value: "foobar" + opts: + keyAltName: "local:name2" + algorithm: "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + expectResult: { "$binary" : { "base64" : "AZaHGpfp2pntvgAAAAAAAAAC4yX2LTAuN253GAkEO2ZXp4GpCyM7yoVNJMQQl+6uzxMs03IprLC7DL2vr18x9LwOimjTS9YbMJhrnFkEPuNhbg==", "subType" : "06" } } + + - description: can explicitly decrypt with a named KMS provider + operations: + - name: decrypt + object: *clientEncryption0 + arguments: + value: { "$binary" : { "base64" : "AZaHGpfp2pntvgAAAAAAAAAC4yX2LTAuN253GAkEO2ZXp4GpCyM7yoVNJMQQl+6uzxMs03IprLC7DL2vr18x9LwOimjTS9YbMJhrnFkEPuNhbg==", "subType" : "06" } } + expectResult: "foobar" + \ No newline at end of file diff --git a/test/spec/client-side-encryption/tests/unified/namedKMS-rewrapManyDataKey.json b/test/spec/client-side-encryption/tests/unified/namedKMS-rewrapManyDataKey.json new file mode 100644 index 0000000000..b3b9bd2477 --- /dev/null +++ b/test/spec/client-side-encryption/tests/unified/namedKMS-rewrapManyDataKey.json @@ -0,0 +1,1385 @@ +{ + "description": "namedKMS-rewrapManyDataKey", + "schemaVersion": "1.18", + "runOnRequirements": [ + { + "csfle": true + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "clientEncryption": { + "id": "clientEncryption0", + "clientEncryptionOpts": { + "keyVaultClient": "client0", + "keyVaultNamespace": "keyvault.datakeys", + "kmsProviders": { + "aws:name1": { + "accessKeyId": { + "$$placeholder": 1 + }, + "secretAccessKey": { + "$$placeholder": 1 + } + }, + "azure:name1": { + "tenantId": { + "$$placeholder": 1 + }, + "clientId": { + "$$placeholder": 1 + }, + "clientSecret": { + "$$placeholder": 1 + } + }, + "gcp:name1": { + "email": { + "$$placeholder": 1 + }, + "privateKey": { + "$$placeholder": 1 + } + }, + "kmip:name1": { + "endpoint": { + "$$placeholder": 1 + } + }, + "local:name1": { + "key": { + "$$placeholder": 1 + } + }, + "local:name2": { + "key": "local+name2+YUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk" + }, + "aws:name2": { + "accessKeyId": { + "$$placeholder": 1 + }, + "secretAccessKey": { + "$$placeholder": 1 + } + } + } + } + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "keyvault" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "datakeys" + } + } + ], + "initialData": [ + { + "databaseName": "keyvault", + "collectionName": "datakeys", + "documents": [ + { + "_id": { + "$binary": { + "base64": "YXdzYXdzYXdzYXdzYXdzYQ==", + "subType": "04" + } + }, + "keyAltNames": [ + "aws:name1_key" + ], + "keyMaterial": { + "$binary": { + "base64": "AQICAHhQNmWG2CzOm1dq3kWLM+iDUZhEqnhJwH9wZVpuZ94A8gFXJqbF0Fy872MD7xl56D/2AAAAwjCBvwYJKoZIhvcNAQcGoIGxMIGuAgEAMIGoBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDO7HPisPUlGzaio9vgIBEIB7/Qow46PMh/8JbEUbdXgTGhLfXPE+KIVW7T8s6YEMlGiRvMu7TV0QCIUJlSHPKZxzlJ2iwuz5yXeOag+EdY+eIQ0RKrsJ3b8UTisZYzGjfzZnxUKLzLoeXremtRCm3x47wCuHKd1dhh6FBbYt5TL2tDaj+vL2GBrKat2L", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "status": 1, + "masterKey": { + "provider": "aws:name1", + "key": "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0", + "region": "us-east-1" + } + }, + { + "_id": { + "$binary": { + "base64": "YXp1cmVhenVyZWF6dXJlYQ==", + "subType": "04" + } + }, + "keyAltNames": [ + "azure:name1_key" + ], + "keyMaterial": { + "$binary": { + "base64": "pr01l7qDygUkFE/0peFwpnNlv3iIy8zrQK38Q9i12UCN2jwZHDmfyx8wokiIKMb9kAleeY+vnt3Cf1MKu9kcDmI+KxbNDd+V3ytAAGzOVLDJr77CiWjF9f8ntkXRHrAY9WwnVDANYkDwXlyU0Y2GQFTiW65jiQhUtYLYH63Tk48SsJuQvnWw1Q+PzY8ga+QeVec8wbcThwtm+r2IHsCFnc72Gv73qq7weISw+O4mN08z3wOp5FOS2ZM3MK7tBGmPdBcktW7F8ODGsOQ1FU53OrWUnyX2aTi2ftFFFMWVHqQo7EYuBZHru8RRODNKMyQk0BFfKovAeTAVRv9WH9QU7g==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "status": 1, + "masterKey": { + "provider": "azure:name1", + "keyVaultEndpoint": "key-vault-csfle.vault.azure.net", + "keyName": "key-name-csfle" + } + }, + { + "_id": { + "$binary": { + "base64": "Z2NwZ2NwZ2NwZ2NwZ2NwZw==", + "subType": "04" + } + }, + "keyAltNames": [ + "gcp:name1_key" + ], + "keyMaterial": { + "$binary": { + "base64": "CiQAIgLj0USbQtof/pYRLQO96yg/JEtZbD1UxKueaC37yzT5tTkSiQEAhClWB5ZCSgzHgxv8raWjNB4r7e8ePGdsmSuYTYmLC5oHHS/BdQisConzNKFaobEQZHamTCjyhy5NotKF8MWoo+dyfQApwI29+vAGyrUIQCXzKwRnNdNQ+lb3vJtS5bqvLTvSxKHpVca2kqyC9nhonV+u4qru5Q2bAqUgVFc8fL4pBuvlowZFTQ==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "status": 1, + "masterKey": { + "provider": "gcp:name1", + "projectId": "devprod-drivers", + "location": "global", + "keyRing": "key-ring-csfle", + "keyName": "key-name-csfle" + } + }, + { + "_id": { + "$binary": { + "base64": "a21pcGttaXBrbWlwa21pcA==", + "subType": "04" + } + }, + "keyAltNames": [ + "kmip:name1_key" + ], + "keyMaterial": { + "$binary": { + "base64": "CklVctHzke4mcytd0TxGqvepkdkQN8NUF4+jV7aZQITAKdz6WjdDpq3lMt9nSzWGG2vAEfvRb3mFEVjV57qqGqxjq2751gmiMRHXz0btStbIK3mQ5xbY9kdye4tsixlCryEwQONr96gwlwKKI9Nubl9/8+uRF6tgYjje7Q7OjauEf1SrJwKcoQ3WwnjZmEqAug0kImCpJ/irhdqPzivRiA==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "status": 1, + "masterKey": { + "provider": "kmip:name1", + "keyId": "1" + } + }, + { + "_id": { + "$binary": { + "base64": "bG9jYWxrZXlsb2NhbGtleQ==", + "subType": "04" + } + }, + "keyAltNames": [ + "local:name1_key" + ], + "keyMaterial": { + "$binary": { + "base64": "ABKBldDEoDW323yejOnIRk6YQmlD9d3eQthd16scKL75nz2LjNL9fgPDZWrFFOlqlhMCFaSrNJfGrFUjYk5JFDO7soG5Syb50k1niJoKg4ilsj0L4mpimFUtTpOr2nzZOeQtvAksEXc7gsFgq8gV7t/U3lsaXPY7I0t42DfSE8EGlPdxRjFdHnxh+OR8h7U9b8Qs5K5UuhgyeyxaBZ1Hgw==", + "subType": "00" + } + }, + "creationDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "updateDate": { + "$date": { + "$numberLong": "1641024000000" + } + }, + "status": 1, + "masterKey": { + "provider": "local:name1" + } + } + ] + } + ], + "tests": [ + { + "description": "rewrap to aws:name1", + "operations": [ + { + "name": "rewrapManyDataKey", + "object": "clientEncryption0", + "arguments": { + "filter": { + "keyAltNames": { + "$ne": "aws:name1_key" + } + }, + "opts": { + "provider": "aws:name1", + "masterKey": { + "key": "arn:aws:kms:us-east-1:579766882180:key/061334ae-07a8-4ceb-a813-8135540e837d", + "region": "us-east-1" + } + } + }, + "expectResult": { + "bulkWriteResult": { + "insertedCount": 0, + "matchedCount": 4, + "modifiedCount": 4, + "deletedCount": 0, + "upsertedCount": 0, + "upsertedIds": {}, + "insertedIds": { + "$$unsetOrMatches": {} + } + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "find": "datakeys", + "filter": { + "keyAltNames": { + "$ne": "aws:name1_key" + } + }, + "readConcern": { + "level": "majority" + } + } + } + }, + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "update": "datakeys", + "ordered": true, + "updates": [ + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "aws:name1", + "key": "arn:aws:kms:us-east-1:579766882180:key/061334ae-07a8-4ceb-a813-8135540e837d", + "region": "us-east-1" + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + }, + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "aws:name1", + "key": "arn:aws:kms:us-east-1:579766882180:key/061334ae-07a8-4ceb-a813-8135540e837d", + "region": "us-east-1" + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + }, + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "aws:name1", + "key": "arn:aws:kms:us-east-1:579766882180:key/061334ae-07a8-4ceb-a813-8135540e837d", + "region": "us-east-1" + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + }, + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "aws:name1", + "key": "arn:aws:kms:us-east-1:579766882180:key/061334ae-07a8-4ceb-a813-8135540e837d", + "region": "us-east-1" + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ], + "writeConcern": { + "w": "majority" + } + } + } + } + ] + } + ] + }, + { + "description": "rewrap to azure:name1", + "operations": [ + { + "name": "rewrapManyDataKey", + "object": "clientEncryption0", + "arguments": { + "filter": { + "keyAltNames": { + "$ne": "azure:name1_key" + } + }, + "opts": { + "provider": "azure:name1", + "masterKey": { + "keyVaultEndpoint": "key-vault-csfle.vault.azure.net", + "keyName": "key-name-csfle" + } + } + }, + "expectResult": { + "bulkWriteResult": { + "insertedCount": 0, + "matchedCount": 4, + "modifiedCount": 4, + "deletedCount": 0, + "upsertedCount": 0, + "upsertedIds": {}, + "insertedIds": { + "$$unsetOrMatches": {} + } + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "find": "datakeys", + "filter": { + "keyAltNames": { + "$ne": "azure:name1_key" + } + }, + "readConcern": { + "level": "majority" + } + } + } + }, + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "update": "datakeys", + "ordered": true, + "updates": [ + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "azure:name1", + "keyVaultEndpoint": "key-vault-csfle.vault.azure.net", + "keyName": "key-name-csfle" + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + }, + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "azure:name1", + "keyVaultEndpoint": "key-vault-csfle.vault.azure.net", + "keyName": "key-name-csfle" + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + }, + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "azure:name1", + "keyVaultEndpoint": "key-vault-csfle.vault.azure.net", + "keyName": "key-name-csfle" + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + }, + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "azure:name1", + "keyVaultEndpoint": "key-vault-csfle.vault.azure.net", + "keyName": "key-name-csfle" + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ], + "writeConcern": { + "w": "majority" + } + } + } + } + ] + } + ] + }, + { + "description": "rewrap to gcp:name1", + "operations": [ + { + "name": "rewrapManyDataKey", + "object": "clientEncryption0", + "arguments": { + "filter": { + "keyAltNames": { + "$ne": "gcp:name1_key" + } + }, + "opts": { + "provider": "gcp:name1", + "masterKey": { + "projectId": "devprod-drivers", + "location": "global", + "keyRing": "key-ring-csfle", + "keyName": "key-name-csfle" + } + } + }, + "expectResult": { + "bulkWriteResult": { + "insertedCount": 0, + "matchedCount": 4, + "modifiedCount": 4, + "deletedCount": 0, + "upsertedCount": 0, + "upsertedIds": {}, + "insertedIds": { + "$$unsetOrMatches": {} + } + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "find": "datakeys", + "filter": { + "keyAltNames": { + "$ne": "gcp:name1_key" + } + }, + "readConcern": { + "level": "majority" + } + } + } + }, + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "update": "datakeys", + "ordered": true, + "updates": [ + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "gcp:name1", + "projectId": "devprod-drivers", + "location": "global", + "keyRing": "key-ring-csfle", + "keyName": "key-name-csfle" + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + }, + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "gcp:name1", + "projectId": "devprod-drivers", + "location": "global", + "keyRing": "key-ring-csfle", + "keyName": "key-name-csfle" + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + }, + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "gcp:name1", + "projectId": "devprod-drivers", + "location": "global", + "keyRing": "key-ring-csfle", + "keyName": "key-name-csfle" + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + }, + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "gcp:name1", + "projectId": "devprod-drivers", + "location": "global", + "keyRing": "key-ring-csfle", + "keyName": "key-name-csfle" + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ], + "writeConcern": { + "w": "majority" + } + } + } + } + ] + } + ] + }, + { + "description": "rewrap to kmip:name1", + "operations": [ + { + "name": "rewrapManyDataKey", + "object": "clientEncryption0", + "arguments": { + "filter": { + "keyAltNames": { + "$ne": "kmip:name1_key" + } + }, + "opts": { + "provider": "kmip:name1" + } + }, + "expectResult": { + "bulkWriteResult": { + "insertedCount": 0, + "matchedCount": 4, + "modifiedCount": 4, + "deletedCount": 0, + "upsertedCount": 0, + "upsertedIds": {}, + "insertedIds": { + "$$unsetOrMatches": {} + } + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "find": "datakeys", + "filter": { + "keyAltNames": { + "$ne": "kmip:name1_key" + } + }, + "readConcern": { + "level": "majority" + } + } + } + }, + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "update": "datakeys", + "ordered": true, + "updates": [ + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "kmip:name1", + "keyId": { + "$$type": "string" + } + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + }, + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "kmip:name1", + "keyId": { + "$$type": "string" + } + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + }, + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "kmip:name1", + "keyId": { + "$$type": "string" + } + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + }, + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "kmip:name1", + "keyId": { + "$$type": "string" + } + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ], + "writeConcern": { + "w": "majority" + } + } + } + } + ] + } + ] + }, + { + "description": "rewrap to local:name1", + "operations": [ + { + "name": "rewrapManyDataKey", + "object": "clientEncryption0", + "arguments": { + "filter": { + "keyAltNames": { + "$ne": "local:name1_key" + } + }, + "opts": { + "provider": "local:name1" + } + }, + "expectResult": { + "bulkWriteResult": { + "insertedCount": 0, + "matchedCount": 4, + "modifiedCount": 4, + "deletedCount": 0, + "upsertedCount": 0, + "upsertedIds": {}, + "insertedIds": { + "$$unsetOrMatches": {} + } + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "find": "datakeys", + "filter": { + "keyAltNames": { + "$ne": "local:name1_key" + } + }, + "readConcern": { + "level": "majority" + } + } + } + }, + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "update": "datakeys", + "ordered": true, + "updates": [ + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "local:name1" + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + }, + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "local:name1" + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + }, + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "local:name1" + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + }, + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "local:name1" + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ], + "writeConcern": { + "w": "majority" + } + } + } + } + ] + } + ] + }, + { + "description": "rewrap from local:name1 to local:name2", + "operations": [ + { + "name": "rewrapManyDataKey", + "object": "clientEncryption0", + "arguments": { + "filter": { + "keyAltNames": { + "$eq": "local:name1_key" + } + }, + "opts": { + "provider": "local:name2" + } + }, + "expectResult": { + "bulkWriteResult": { + "insertedCount": 0, + "matchedCount": 1, + "modifiedCount": 1, + "deletedCount": 0, + "upsertedCount": 0, + "upsertedIds": {}, + "insertedIds": { + "$$unsetOrMatches": {} + } + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "find": "datakeys", + "filter": { + "keyAltNames": { + "$eq": "local:name1_key" + } + }, + "readConcern": { + "level": "majority" + } + } + } + }, + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "update": "datakeys", + "ordered": true, + "updates": [ + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "local:name2" + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ], + "writeConcern": { + "w": "majority" + } + } + } + } + ] + } + ] + }, + { + "description": "rewrap from aws:name1 to aws:name2", + "operations": [ + { + "name": "rewrapManyDataKey", + "object": "clientEncryption0", + "arguments": { + "filter": { + "keyAltNames": { + "$eq": "aws:name1_key" + } + }, + "opts": { + "provider": "aws:name2", + "masterKey": { + "key": "arn:aws:kms:us-east-1:857654397073:key/0f8468f0-f135-4226-aa0b-bd05c4c30df5", + "region": "us-east-1" + } + } + }, + "expectResult": { + "bulkWriteResult": { + "insertedCount": 0, + "matchedCount": 1, + "modifiedCount": 1, + "deletedCount": 0, + "upsertedCount": 0, + "upsertedIds": {}, + "insertedIds": { + "$$unsetOrMatches": {} + } + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "find": "datakeys", + "filter": { + "keyAltNames": { + "$eq": "aws:name1_key" + } + }, + "readConcern": { + "level": "majority" + } + } + } + }, + { + "commandStartedEvent": { + "databaseName": "keyvault", + "command": { + "update": "datakeys", + "ordered": true, + "updates": [ + { + "q": { + "_id": { + "$$type": "binData" + } + }, + "u": { + "$set": { + "masterKey": { + "provider": "aws:name2", + "key": "arn:aws:kms:us-east-1:857654397073:key/0f8468f0-f135-4226-aa0b-bd05c4c30df5", + "region": "us-east-1" + }, + "keyMaterial": { + "$$type": "binData" + } + }, + "$currentDate": { + "updateDate": true + } + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ], + "writeConcern": { + "w": "majority" + } + } + } + } + ] + } + ] + } + ] +} diff --git a/test/spec/client-side-encryption/tests/unified/namedKMS-rewrapManyDataKey.yml b/test/spec/client-side-encryption/tests/unified/namedKMS-rewrapManyDataKey.yml new file mode 100644 index 0000000000..b776ed4dda --- /dev/null +++ b/test/spec/client-side-encryption/tests/unified/namedKMS-rewrapManyDataKey.yml @@ -0,0 +1,432 @@ +description: namedKMS-rewrapManyDataKey + +schemaVersion: "1.18" + +runOnRequirements: + - csfle: true + +createEntities: + - client: + id: &client0 client0 + observeEvents: + - commandStartedEvent + - clientEncryption: + id: &clientEncryption0 clientEncryption0 + clientEncryptionOpts: + keyVaultClient: *client0 + keyVaultNamespace: keyvault.datakeys + kmsProviders: + "aws:name1": { accessKeyId: { $$placeholder: 1 }, secretAccessKey: { $$placeholder: 1 } } + "azure:name1": { tenantId: { $$placeholder: 1 }, clientId: { $$placeholder: 1 }, clientSecret: { $$placeholder: 1 } } + "gcp:name1": { email: { $$placeholder: 1 }, privateKey: { $$placeholder: 1 } } + "kmip:name1": { endpoint: { $$placeholder: 1 } } + "local:name1": { key: { $$placeholder: 1 } } + # Use a different local master key for `local:name2`. + "local:name2": { key: "local+name2+YUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk" } + # Use a different AWS account to test wrapping between different AWS accounts. + "aws:name2": { accessKeyId: { $$placeholder: 1 }, secretAccessKey: { $$placeholder: 1 } } + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name keyvault + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name datakeys + +initialData: + - databaseName: *database0Name + collectionName: *collection0Name + documents: + - _id: &aws:name1_key_id { $binary: { base64: YXdzYXdzYXdzYXdzYXdzYQ==, subType: "04" } } + keyAltNames: ["aws:name1_key"] + keyMaterial: { $binary: { base64: AQICAHhQNmWG2CzOm1dq3kWLM+iDUZhEqnhJwH9wZVpuZ94A8gFXJqbF0Fy872MD7xl56D/2AAAAwjCBvwYJKoZIhvcNAQcGoIGxMIGuAgEAMIGoBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDO7HPisPUlGzaio9vgIBEIB7/Qow46PMh/8JbEUbdXgTGhLfXPE+KIVW7T8s6YEMlGiRvMu7TV0QCIUJlSHPKZxzlJ2iwuz5yXeOag+EdY+eIQ0RKrsJ3b8UTisZYzGjfzZnxUKLzLoeXremtRCm3x47wCuHKd1dhh6FBbYt5TL2tDaj+vL2GBrKat2L, subType: "00" } } + creationDate: { $date: { $numberLong: "1641024000000" } } + updateDate: { $date: { $numberLong: "1641024000000" } } + status: 1 + masterKey: &aws:name1_masterkey + provider: aws:name1 + key: "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0" + region: us-east-1 + - _id: &azure:name1_key_id { $binary: { base64: YXp1cmVhenVyZWF6dXJlYQ==, subType: "04" } } + keyAltNames: ["azure:name1_key"] + keyMaterial: { $binary: { base64: pr01l7qDygUkFE/0peFwpnNlv3iIy8zrQK38Q9i12UCN2jwZHDmfyx8wokiIKMb9kAleeY+vnt3Cf1MKu9kcDmI+KxbNDd+V3ytAAGzOVLDJr77CiWjF9f8ntkXRHrAY9WwnVDANYkDwXlyU0Y2GQFTiW65jiQhUtYLYH63Tk48SsJuQvnWw1Q+PzY8ga+QeVec8wbcThwtm+r2IHsCFnc72Gv73qq7weISw+O4mN08z3wOp5FOS2ZM3MK7tBGmPdBcktW7F8ODGsOQ1FU53OrWUnyX2aTi2ftFFFMWVHqQo7EYuBZHru8RRODNKMyQk0BFfKovAeTAVRv9WH9QU7g==, subType: "00" } } + creationDate: { $date: { $numberLong: "1641024000000" } } + updateDate: { $date: { $numberLong: "1641024000000" } } + status: 1 + masterKey: &azure:name1_masterkey + provider: "azure:name1" + keyVaultEndpoint: key-vault-csfle.vault.azure.net + keyName: key-name-csfle + - _id: &gcp:name1_key_id { $binary: { base64: Z2NwZ2NwZ2NwZ2NwZ2NwZw==, subType: "04" } } + keyAltNames: ["gcp:name1_key"] + keyMaterial: { $binary: { base64: CiQAIgLj0USbQtof/pYRLQO96yg/JEtZbD1UxKueaC37yzT5tTkSiQEAhClWB5ZCSgzHgxv8raWjNB4r7e8ePGdsmSuYTYmLC5oHHS/BdQisConzNKFaobEQZHamTCjyhy5NotKF8MWoo+dyfQApwI29+vAGyrUIQCXzKwRnNdNQ+lb3vJtS5bqvLTvSxKHpVca2kqyC9nhonV+u4qru5Q2bAqUgVFc8fL4pBuvlowZFTQ==, subType: "00" } } + creationDate: { $date: { $numberLong: "1641024000000" } } + updateDate: { $date: { $numberLong: "1641024000000" } } + status: 1 + masterKey: &gcp:name1_masterkey + provider: "gcp:name1" + projectId: devprod-drivers + location: global + keyRing: key-ring-csfle + keyName: key-name-csfle + - _id: &kmip:name1_key_id { $binary: { base64: a21pcGttaXBrbWlwa21pcA==, subType: "04" } } + keyAltNames: ["kmip:name1_key"] + keyMaterial: { $binary: { base64: CklVctHzke4mcytd0TxGqvepkdkQN8NUF4+jV7aZQITAKdz6WjdDpq3lMt9nSzWGG2vAEfvRb3mFEVjV57qqGqxjq2751gmiMRHXz0btStbIK3mQ5xbY9kdye4tsixlCryEwQONr96gwlwKKI9Nubl9/8+uRF6tgYjje7Q7OjauEf1SrJwKcoQ3WwnjZmEqAug0kImCpJ/irhdqPzivRiA==, subType: "00" } } + creationDate: { $date: { $numberLong: "1641024000000" } } + updateDate: { $date: { $numberLong: "1641024000000" } } + status: 1 + masterKey: &kmip:name1_masterkey + provider: "kmip:name1" + keyId: "1" + - _id: &local:name1_key_id { $binary: { base64: bG9jYWxrZXlsb2NhbGtleQ==, subType: "04" } } + keyAltNames: ["local:name1_key"] + keyMaterial: { $binary: { base64: ABKBldDEoDW323yejOnIRk6YQmlD9d3eQthd16scKL75nz2LjNL9fgPDZWrFFOlqlhMCFaSrNJfGrFUjYk5JFDO7soG5Syb50k1niJoKg4ilsj0L4mpimFUtTpOr2nzZOeQtvAksEXc7gsFgq8gV7t/U3lsaXPY7I0t42DfSE8EGlPdxRjFdHnxh+OR8h7U9b8Qs5K5UuhgyeyxaBZ1Hgw==, subType: "00" } } + creationDate: { $date: { $numberLong: "1641024000000" } } + updateDate: { $date: { $numberLong: "1641024000000" } } + status: 1 + masterKey: &local:name1_masterkey + provider: "local:name1" + +tests: + - description: "rewrap to aws:name1" + operations: + - name: rewrapManyDataKey + object: *clientEncryption0 + arguments: + filter: { keyAltNames: { $ne: "aws:name1_key" } } + opts: + provider: "aws:name1" + # Different key: 89fcc2c4-08b0-4bd9-9f25-e30687b580d0 -> 061334ae-07a8-4ceb-a813-8135540e837d. + masterKey: &new_aws_masterkey + key: "arn:aws:kms:us-east-1:579766882180:key/061334ae-07a8-4ceb-a813-8135540e837d" + region: us-east-1 + expectResult: + bulkWriteResult: + insertedCount: 0 + matchedCount: 4 + modifiedCount: 4 + deletedCount: 0 + upsertedCount: 0 + upsertedIds: {} + insertedIds: { $$unsetOrMatches: {} } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + databaseName: *database0Name + command: + find: *collection0Name + filter: { keyAltNames: { $ne: "aws:name1_key" } } + readConcern: { level: majority } + - commandStartedEvent: + databaseName: *database0Name + command: + update: *collection0Name + ordered: true + updates: + - q: { _id: { $$type: binData } } + u: { $set: { masterKey: { provider: "aws:name1", <<: *new_aws_masterkey }, keyMaterial: { $$type: binData } }, $currentDate: { updateDate: true } } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + - q: { _id: { $$type: binData } } + u: { $set: { masterKey: { provider: "aws:name1", <<: *new_aws_masterkey }, keyMaterial: { $$type: binData } }, $currentDate: { updateDate: true } } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + - q: { _id: { $$type: binData } } + u: { $set: { masterKey: { provider: "aws:name1", <<: *new_aws_masterkey }, keyMaterial: { $$type: binData } }, $currentDate: { updateDate: true } } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + - q: { _id: { $$type: binData } } + u: { $set: { masterKey: { provider: "aws:name1", <<: *new_aws_masterkey }, keyMaterial: { $$type: binData } }, $currentDate: { updateDate: true } } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + writeConcern: { w: majority } + + - description: "rewrap to azure:name1" + operations: + - name: rewrapManyDataKey + object: *clientEncryption0 + arguments: + filter: { keyAltNames: { $ne: azure:name1_key } } + opts: + provider: "azure:name1" + masterKey: &new_azure_masterkey + keyVaultEndpoint: key-vault-csfle.vault.azure.net + keyName: key-name-csfle + expectResult: + bulkWriteResult: + insertedCount: 0 + matchedCount: 4 + modifiedCount: 4 + deletedCount: 0 + upsertedCount: 0 + upsertedIds: {} + insertedIds: { $$unsetOrMatches: {} } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + databaseName: *database0Name + command: + find: *collection0Name + filter: { keyAltNames: { $ne: azure:name1_key } } + readConcern: { level: majority } + - commandStartedEvent: + databaseName: *database0Name + command: + update: *collection0Name + ordered: true + updates: + - q: { _id: { $$type: binData } } + u: { $set: { masterKey: { provider: "azure:name1", <<: *new_azure_masterkey }, keyMaterial: { $$type: binData } }, $currentDate: { updateDate: true } } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + - q: { _id: { $$type: binData } } + u: { $set: { masterKey: { provider: "azure:name1", <<: *new_azure_masterkey }, keyMaterial: { $$type: binData } }, $currentDate: { updateDate: true } } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + - q: { _id: { $$type: binData } } + u: { $set: { masterKey: { provider: "azure:name1", <<: *new_azure_masterkey }, keyMaterial: { $$type: binData } }, $currentDate: { updateDate: true } } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + - q: { _id: { $$type: binData } } + u: { $set: { masterKey: { provider: "azure:name1", <<: *new_azure_masterkey }, keyMaterial: { $$type: binData } }, $currentDate: { updateDate: true } } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + writeConcern: { w: majority } + + - description: "rewrap to gcp:name1" + operations: + - name: rewrapManyDataKey + object: *clientEncryption0 + arguments: + filter: { keyAltNames: { $ne: gcp:name1_key } } + opts: + provider: "gcp:name1" + masterKey: &new_gcp_masterkey + projectId: devprod-drivers + location: global + keyRing: key-ring-csfle + keyName: key-name-csfle + expectResult: + bulkWriteResult: + insertedCount: 0 + matchedCount: 4 + modifiedCount: 4 + deletedCount: 0 + upsertedCount: 0 + upsertedIds: {} + insertedIds: { $$unsetOrMatches: {} } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + databaseName: *database0Name + command: + find: *collection0Name + filter: { keyAltNames: { $ne: "gcp:name1_key" } } + readConcern: { level: majority } + - commandStartedEvent: + databaseName: *database0Name + command: + update: *collection0Name + ordered: true + updates: + - q: { _id: { $$type: binData } } + u: { $set: { masterKey: { provider: "gcp:name1", <<: *new_gcp_masterkey }, keyMaterial: { $$type: binData } }, $currentDate: { updateDate: true } } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + - q: { _id: { $$type: binData } } + u: { $set: { masterKey: { provider: "gcp:name1", <<: *new_gcp_masterkey }, keyMaterial: { $$type: binData } }, $currentDate: { updateDate: true } } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + - q: { _id: { $$type: binData } } + u: { $set: { masterKey: { provider: "gcp:name1", <<: *new_gcp_masterkey }, keyMaterial: { $$type: binData } }, $currentDate: { updateDate: true } } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + - q: { _id: { $$type: binData } } + u: { $set: { masterKey: { provider: "gcp:name1", <<: *new_gcp_masterkey }, keyMaterial: { $$type: binData } }, $currentDate: { updateDate: true } } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + writeConcern: { w: majority } + + - description: "rewrap to kmip:name1" + operations: + - name: rewrapManyDataKey + object: *clientEncryption0 + arguments: + filter: { keyAltNames: { $ne: "kmip:name1_key" } } + opts: + provider: "kmip:name1" + expectResult: + bulkWriteResult: + insertedCount: 0 + matchedCount: 4 + modifiedCount: 4 + deletedCount: 0 + upsertedCount: 0 + upsertedIds: {} + insertedIds: { $$unsetOrMatches: {} } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + databaseName: *database0Name + command: + find: *collection0Name + filter: { keyAltNames: { $ne: "kmip:name1_key" } } + readConcern: { level: majority } + - commandStartedEvent: + databaseName: *database0Name + command: + update: *collection0Name + ordered: true + updates: + - q: { _id: { $$type: binData } } + u: { $set: { masterKey: { provider: "kmip:name1", keyId: { $$type: string } }, keyMaterial: { $$type: binData } }, $currentDate: { updateDate: true } } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + - q: { _id: { $$type: binData } } + u: { $set: { masterKey: { provider: "kmip:name1", keyId: { $$type: string } }, keyMaterial: { $$type: binData } }, $currentDate: { updateDate: true } } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + - q: { _id: { $$type: binData } } + u: { $set: { masterKey: { provider: "kmip:name1", keyId: { $$type: string } }, keyMaterial: { $$type: binData } }, $currentDate: { updateDate: true } } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + - q: { _id: { $$type: binData } } + u: { $set: { masterKey: { provider: "kmip:name1", keyId: { $$type: string } }, keyMaterial: { $$type: binData } }, $currentDate: { updateDate: true } } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + writeConcern: { w: majority } + + - description: "rewrap to local:name1" + operations: + - name: rewrapManyDataKey + object: *clientEncryption0 + arguments: + filter: { keyAltNames: { $ne: "local:name1_key" } } + opts: + provider: "local:name1" + expectResult: + bulkWriteResult: + insertedCount: 0 + matchedCount: 4 + modifiedCount: 4 + deletedCount: 0 + upsertedCount: 0 + upsertedIds: {} + insertedIds: { $$unsetOrMatches: {} } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + databaseName: *database0Name + command: + find: *collection0Name + filter: { keyAltNames: { $ne: "local:name1_key" } } + readConcern: { level: majority } + - commandStartedEvent: + databaseName: *database0Name + command: + update: *collection0Name + ordered: true + updates: + - q: { _id: { $$type: binData } } + u: { $set: { masterKey: { provider: "local:name1" }, keyMaterial: { $$type: binData } }, $currentDate: { updateDate: true } } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + - q: { _id: { $$type: binData } } + u: { $set: { masterKey: { provider: "local:name1" }, keyMaterial: { $$type: binData } }, $currentDate: { updateDate: true } } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + - q: { _id: { $$type: binData } } + u: { $set: { masterKey: { provider: "local:name1" }, keyMaterial: { $$type: binData } }, $currentDate: { updateDate: true } } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + - q: { _id: { $$type: binData } } + u: { $set: { masterKey: { provider: "local:name1" }, keyMaterial: { $$type: binData } }, $currentDate: { updateDate: true } } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + writeConcern: { w: majority } + + - description: "rewrap from local:name1 to local:name2" + operations: + - name: rewrapManyDataKey + object: *clientEncryption0 + arguments: + filter: { keyAltNames: { $eq: "local:name1_key" } } + opts: + provider: "local:name2" + expectResult: + bulkWriteResult: + insertedCount: 0 + matchedCount: 1 + modifiedCount: 1 + deletedCount: 0 + upsertedCount: 0 + upsertedIds: {} + insertedIds: { $$unsetOrMatches: {} } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + databaseName: *database0Name + command: + find: *collection0Name + filter: { keyAltNames: { $eq: "local:name1_key" } } + readConcern: { level: majority } + - commandStartedEvent: + databaseName: *database0Name + command: + update: *collection0Name + ordered: true + updates: + - q: { _id: { $$type: binData } } + u: { $set: { masterKey: { provider: "local:name2" }, keyMaterial: { $$type: binData } }, $currentDate: { updateDate: true } } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + writeConcern: { w: majority } + + - description: "rewrap from aws:name1 to aws:name2" + operations: + - name: rewrapManyDataKey + object: *clientEncryption0 + arguments: + filter: { keyAltNames: { $eq: "aws:name1_key" } } + opts: + provider: "aws:name2" + masterKey: &new_awsname2_masterkey + # aws:name1 account does not have permissions to access this key. + key: "arn:aws:kms:us-east-1:857654397073:key/0f8468f0-f135-4226-aa0b-bd05c4c30df5" + region: us-east-1 + expectResult: + bulkWriteResult: + insertedCount: 0 + matchedCount: 1 + modifiedCount: 1 + deletedCount: 0 + upsertedCount: 0 + upsertedIds: {} + insertedIds: { $$unsetOrMatches: {} } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + databaseName: *database0Name + command: + find: *collection0Name + filter: { keyAltNames: { $eq: "aws:name1_key" } } + readConcern: { level: majority } + - commandStartedEvent: + databaseName: *database0Name + command: + update: *collection0Name + ordered: true + updates: + - q: { _id: { $$type: binData } } + u: { $set: { masterKey: { provider: "aws:name2", <<: *new_awsname2_masterkey }, keyMaterial: { $$type: binData } }, $currentDate: { updateDate: true } } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + writeConcern: { w: majority } diff --git a/test/tools/runner/config.ts b/test/tools/runner/config.ts index c15f7a99ab..d97f003e19 100644 --- a/test/tools/runner/config.ts +++ b/test/tools/runner/config.ts @@ -56,7 +56,12 @@ function convertToConnStringMap(obj: Record) { export class TestConfiguration { version: string; - clientSideEncryption: Record; + clientSideEncryption: { + enabled: boolean; + mongodbClientEncryption: any; + CSFLE_KMS_PROVIDERS: string | undefined; + version: string; + }; parameters: Record; singleMongosLoadBalancerUri: string; multiMongosLoadBalancerUri: string; diff --git a/test/tools/runner/filters/client_encryption_filter.ts b/test/tools/runner/filters/client_encryption_filter.ts index b666b38039..f9f2ec9093 100644 --- a/test/tools/runner/filters/client_encryption_filter.ts +++ b/test/tools/runner/filters/client_encryption_filter.ts @@ -72,9 +72,11 @@ export class ClientSideEncryptionFilter extends Filter { } const validRange = typeof clientSideEncryption === 'string' ? clientSideEncryption : '>=0.0.0'; + if (ClientSideEncryptionFilter.version && !this.enabled) + return 'Test requires FLE environment variables..'; if (!this.enabled) return 'Test requires CSFLE to be enabled.'; return satisfies(ClientSideEncryptionFilter.version, validRange) ? true - : `requires mongodb-client-encryption ${validRange}`; + : `requires mongodb-client-encryption ${validRange}, received ${ClientSideEncryptionFilter.version}`; } } diff --git a/test/tools/spec-runner/index.js b/test/tools/spec-runner/index.js index 1e1c1f5523..3fb15d783f 100644 --- a/test/tools/spec-runner/index.js +++ b/test/tools/spec-runner/index.js @@ -89,6 +89,16 @@ function translateClientOptions(options) { }; } + if (options.autoEncryptOpts.kmsProviders['local:name2']) { + kmsProviders['local:name2'] = options.autoEncryptOpts.kmsProviders['local:name2']; + options.autoEncryption.tlsOptions = { + 'local:name2': { + tlsCAFile: process.env.KMIP_TLS_CA_FILE, + tlsCertificateKeyFile: process.env.KMIP_TLS_CERT_FILE + } + }; + } + if (process.env.CRYPT_SHARED_LIB_PATH) { options.autoEncryption.extraOptions = { cryptSharedLibPath: process.env.CRYPT_SHARED_LIB_PATH diff --git a/test/tools/unified-spec-runner/operations.ts b/test/tools/unified-spec-runner/operations.ts index 0619a5c111..9d71dddf55 100644 --- a/test/tools/unified-spec-runner/operations.ts +++ b/test/tools/unified-spec-runner/operations.ts @@ -867,6 +867,20 @@ operations.set('getKeyByAltName', async ({ entities, operation }) => { return clientEncryption.getKeyByAltName(keyAltName); }); +operations.set('encrypt', async ({ entities, operation }) => { + const clientEncryption = entities.getEntity('clientEncryption', operation.object); + const { value, opts } = operation.arguments ?? {}; + + return clientEncryption.encrypt(value, opts); +}); + +operations.set('decrypt', async ({ entities, operation }) => { + const clientEncryption = entities.getEntity('clientEncryption', operation.object); + const { value } = operation.arguments ?? {}; + + return clientEncryption.decrypt(value); +}); + operations.set('listSearchIndexes', async ({ entities, operation }) => { const collection: Collection = entities.getEntity('collection', operation.object); const { name, aggregationOptions } = operation.arguments ?? {}; diff --git a/test/tools/unified-spec-runner/schema.ts b/test/tools/unified-spec-runner/schema.ts index ea331ce691..8917426292 100644 --- a/test/tools/unified-spec-runner/schema.ts +++ b/test/tools/unified-spec-runner/schema.ts @@ -182,35 +182,44 @@ export interface StreamEntity { export type StringOrPlaceholder = string | { $$placeholder: number }; +type UnnamedKMSProviders = { + aws?: { + accessKeyId: StringOrPlaceholder; + secretAccessKey: StringOrPlaceholder; + sessionToken: StringOrPlaceholder; + }; + azure?: { + tenantId: StringOrPlaceholder; + clientId: StringOrPlaceholder; + clientSecret: StringOrPlaceholder; + identityPlatformEndpoint: StringOrPlaceholder; + }; + gcp?: { + email: StringOrPlaceholder; + privateKey: StringOrPlaceholder; + endPoint: StringOrPlaceholder; + }; + kmip?: { + endpoint: StringOrPlaceholder; + }; + local?: { + key: StringOrPlaceholder; + }; +}; export interface ClientEncryptionEntity { id: string; clientEncryptionOpts: { /** this is the id of the client entity to use as the keyvault client */ keyVaultClient: string; keyVaultNamespace: string; - kmsProviders: { - aws?: { - accessKeyId: StringOrPlaceholder; - secretAccessKey: StringOrPlaceholder; - sessionToken: StringOrPlaceholder; - }; - azure?: { - tenantId: StringOrPlaceholder; - clientId: StringOrPlaceholder; - clientSecret: StringOrPlaceholder; - identityPlatformEndpoint: StringOrPlaceholder; - }; - gcp?: { - email: StringOrPlaceholder; - privateKey: StringOrPlaceholder; - endPoint: StringOrPlaceholder; - }; - kmip?: { - endpoint: StringOrPlaceholder; - }; - local?: { - key: StringOrPlaceholder; - }; + kmsProviders: UnnamedKMSProviders & { + [key: string]: + | UnnamedKMSProviders['aws'] + | UnnamedKMSProviders['gcp'] + | UnnamedKMSProviders['azure'] + | UnnamedKMSProviders['kmip'] + | UnnamedKMSProviders['local'] + | undefined; }; }; } diff --git a/test/tools/unified-spec-runner/unified-utils.ts b/test/tools/unified-spec-runner/unified-utils.ts index 4519a50983..2f36409875 100644 --- a/test/tools/unified-spec-runner/unified-utils.ts +++ b/test/tools/unified-spec-runner/unified-utils.ts @@ -309,44 +309,183 @@ export function getCSFLETestDataFromEnvironment(environment: Record { const isPlaceholderValue = (value: StringOrPlaceholder) => - typeof value !== 'string' && typeof value.$$placeholder !== 'undefined'; + value && typeof value !== 'string' && typeof value.$$placeholder !== 'undefined'; + + const providers: Document = {}; + + function parseAWS(env, test) { + const awsProviders = {}; + test['accessKeyId'] && + (awsProviders['accessKeyId'] = isPlaceholderValue(test['accessKeyId']) + ? env['accessKeyId'] + : test['accessKeyId']); + + test['secretAccessKey'] && + (awsProviders['secretAccessKey'] = isPlaceholderValue(test['secretAccessKey']) + ? env['secretAccessKey'] + : test['secretAccessKey']); + + test['sessionToken'] && + (awsProviders['sessionToken'] = isPlaceholderValue(test['sessionToken']) + ? env['sessionToken'] + : test['sessionToken']); + return awsProviders; + } + function parseAzure(env, test) { + const azureProviders: Document = {}; + test['tenantId'] && + (azureProviders['tenantId'] = isPlaceholderValue(test['tenantId']) + ? env['tenantId'] + : test['tenantId']); + + test['clientId'] && + (azureProviders['clientId'] = isPlaceholderValue(test['clientId']) + ? env['clientId'] + : test['clientId']); + + test['clientSecret'] && + (azureProviders['clientSecret'] = isPlaceholderValue(test['clientSecret']) + ? env['clientSecret'] + : test['clientSecret']); + + test['identityPlatformEndpoint'] && + (azureProviders['identityPlatformEndpoint'] = isPlaceholderValue( + test['identityPlatformEndpoint'] + ) + ? env['identityPlatformEndpoint'] + : test['identityPlatformEndpoint']); + + return azureProviders; + } - const options = {}; + function parseGCP(env, test) { + const gcpProviders = {}; + test['email'] && + (gcpProviders['email'] = isPlaceholderValue(test['email']) ? env['email'] : test['email']); + + test['privateKey'] && + (gcpProviders['privateKey'] = isPlaceholderValue(test['privateKey']) + ? env['privateKey'] + : test['privateKey']); + + test['endPoint'] && + (gcpProviders['endPoint'] = isPlaceholderValue(test['endPoint']) + ? env['endPoint'] + : test['endPoint']); + return gcpProviders; + } + function parseLocal(env, test) { + const localProviders = {}; + test['key'] && + (localProviders['key'] = isPlaceholderValue(test['key']) ? env['key'] : test['key']); + return localProviders; + } + function parseKMIP(env, test) { + const localProviders = {}; + test['endpoint'] && + (localProviders['endpoint'] = isPlaceholderValue(test['endpoint']) + ? env['endpoint'] + : test['endpoint']); + + return localProviders; + } + if ('aws' in kmsProvidersFromTest) { + const env = kmsProvidersFromEnvironment['aws']; + const fromTest = kmsProvidersFromTest['aws']; - const validKMSProviders: Array = [ - 'kmip', - 'local', - 'aws', - 'azure', - 'gcp' - ]; + providers['aws'] = parseAWS(env, fromTest); + } - for (const provider of validKMSProviders) { - if (!(provider in kmsProvidersFromTest)) continue; + if ('aws:name1' in kmsProvidersFromTest) { + const env = kmsProvidersFromEnvironment['aws']; + const fromTest = kmsProvidersFromTest['aws:name1']; - const providerDataFromTest = kmsProvidersFromTest[provider]; - const providerDataFromEnvironment = kmsProvidersFromEnvironment[provider]; + providers['aws:name1'] = parseAWS(env, fromTest); + } - const providerOptions = {}; + if ('aws:name2' in kmsProvidersFromTest) { + providers['aws:name2'] = { + accessKeyId: process.env.FLE_AWS_KEY2, + secretAccessKey: process.env.FLE_AWS_SECRET2 + }; + } - for (const [key, value] of Object.entries(providerDataFromTest ?? {})) { - if (isPlaceholderValue(value)) { - providerOptions[key] = providerDataFromEnvironment[key]; - } else { - providerOptions[key] = value; - } - } + if ('azure' in kmsProvidersFromTest) { + const env = kmsProvidersFromEnvironment['azure']; + const fromTest = kmsProvidersFromTest['azure']; + + providers['azure'] = parseAzure(env, fromTest); + } + + if ('azure:name1' in kmsProvidersFromTest) { + const env = kmsProvidersFromEnvironment['azure']; + const fromTest = kmsProvidersFromTest['azure:name1']; + + providers['azure:name1'] = parseAzure(env, fromTest); + } + + if ('gcp' in kmsProvidersFromTest) { + const env = kmsProvidersFromEnvironment['gcp']; + const fromTest = kmsProvidersFromTest['gcp']; + + providers['gcp'] = parseGCP(env, fromTest); + } + + if ('gcp:name1' in kmsProvidersFromTest) { + const env = kmsProvidersFromEnvironment['gcp']; + const fromTest = kmsProvidersFromTest['gcp:name1']; - options[provider] = providerOptions; + providers['gcp:name1'] = parseGCP(env, fromTest); } - return options; + if ('local' in kmsProvidersFromTest) { + const env = kmsProvidersFromEnvironment['local']; + const fromTest = kmsProvidersFromTest['local']; + + providers['local'] = parseLocal(env, fromTest); + } + + if ('local:name1' in kmsProvidersFromTest) { + const env = kmsProvidersFromEnvironment['local']; + const fromTest = kmsProvidersFromTest['local:name1']; + + providers['local:name1'] = parseLocal(env, fromTest); + } + + if ('local:name2' in kmsProvidersFromTest) { + const env = kmsProvidersFromEnvironment['local']; + const fromTest = kmsProvidersFromTest['local:name2']; + + providers['local:name2'] = parseLocal(env, fromTest); + } + + if ('kmip' in kmsProvidersFromTest) { + const env = kmsProvidersFromEnvironment['kmip']; + const fromTest = kmsProvidersFromTest['kmip']; + + providers['kmip'] = parseKMIP(env, fromTest); + } + + if ('kmip:name1' in kmsProvidersFromTest) { + const env = kmsProvidersFromEnvironment['kmip']; + const fromTest = kmsProvidersFromTest['kmip:name1']; + + providers['kmip:name1'] = parseKMIP(env, fromTest); + } + + return providers; } export function createClientEncryption( @@ -373,13 +512,38 @@ export function createClientEncryption( process.env ); + function parseTLSOptions() { + const handlers: Record = { + aws: 'aws', + 'aws:name1': 'aws', + 'aws:name2': 'aws', + azure: 'azure', + 'azure:name1': 'azure', + gcp: 'gcp', + 'gcp:name1': 'gcp', + local: 'local', + 'local:name1': 'local', + 'local:name2': 'local', + kmip: 'kmip', + 'kmip:name1': 'kmip' + }; + + return Object.keys(kmsProvidersFromTest).reduce((accum, provider) => { + const rootProvider = handlers[provider]; + if (rootProvider && rootProvider in tlsOptions) { + accum[provider] = tlsOptions[rootProvider]; + } + return accum; + }, {}); + } + const kmsProviders = mergeKMSProviders(kmsProvidersFromTest, kmsProvidersFromEnvironment); const autoEncryptionOptions: AutoEncryptionOptions = { keyVaultClient: clientEntity, kmsProviders, keyVaultNamespace, - tlsOptions + tlsOptions: parseTLSOptions() }; if (process.env.CRYPT_SHARED_LIB_PATH) { diff --git a/test/types/client-side-encryption.test-d.ts b/test/types/client-side-encryption.test-d.ts index d9ba7a65c3..6f403668e8 100644 --- a/test/types/client-side-encryption.test-d.ts +++ b/test/types/client-side-encryption.test-d.ts @@ -15,7 +15,7 @@ type RequiredCreateEncryptedCollectionSettings = Parameters< >[2]; expectError({}); -expectError({ +expectAssignable({ provider: 'blah!', createCollectionOptions: { encryptedFields: {} } });