Skip to content

feat(NODE-6952): support configuring DEK cache expiration #4538

New issue

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

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

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
May 13, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@
"js-yaml": "^4.1.0",
"mocha": "^10.8.2",
"mocha-sinon": "^2.1.2",
"mongodb-client-encryption": "^6.3.0",
"mongodb-client-encryption": "^6.4.0",
"mongodb-legacy": "^6.1.3",
"nyc": "^15.1.0",
"prettier": "^3.5.3",
Expand Down
8 changes: 8 additions & 0 deletions src/client-side-encryption/auto_encrypter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,10 @@ export interface AutoEncryptionOptions {
bypassAutoEncryption?: boolean;
/** Allows users to bypass query analysis */
bypassQueryAnalysis?: boolean;
/**
* Sets the expiration time for the DEK in the cache in milliseconds. Defaults to 60000. 0 means no timeout.
*/
keyExpirationMS?: number;
options?: {
/** An optional hook to catch logging messages from the underlying encryption engine */
logger?: (level: AutoEncryptionLoggerLevel, message: string) => void;
Expand Down Expand Up @@ -285,6 +289,10 @@ export class AutoEncrypter {
mongoCryptOptions.bypassQueryAnalysis = options.bypassQueryAnalysis;
}

if (options.keyExpirationMS != null) {
mongoCryptOptions.keyExpirationMS = options.keyExpirationMS;
}

this._bypassMongocryptdAndCryptShared = this._bypassEncryption || !!options.bypassQueryAnalysis;

if (options.extraOptions && options.extraOptions.cryptSharedLibSearchPaths) {
Expand Down
5 changes: 5 additions & 0 deletions src/client-side-encryption/client_encryption.ts
Original file line number Diff line number Diff line change
Expand Up @@ -885,6 +885,11 @@ export interface ClientEncryptionOptions {
*/
tlsOptions?: CSFLEKMSTlsOptions;

/**
* Sets the expiration time for the DEK in the cache in milliseconds. Defaults to 60000. 0 means no timeout.
*/
keyExpirationMS?: number;

/**
* @experimental
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,6 @@ const skippedAuthTests = [
'Insert a document with auto encryption using the AWS provider with temporary credentials',
'Insert a document with auto encryption using Azure KMS provider',
'$rename works if target value has same encryption options',
'Insert with deterministic encryption, then find it',
'Insert with randomized encryption, then find it',
'Bulk write with encryption',
'Insert with bypassAutoEncryption',
'Insert with bypassAutoEncryption for local schema',
Expand Down Expand Up @@ -109,6 +107,13 @@ describe('Client Side Encryption (Legacy)', function () {
if (typeof result === 'string') return result;
}

if (['Insert with deterministic encryption, then find it'].includes(description)) {
const result = configuration.filters.ClientSideEncryptionFilter.filter({
metadata: { requires: { clientSideEncryption: '>=6.4.0' } }
});

if (typeof result === 'string') return result;
}
return true;
});
});
Expand Down Expand Up @@ -142,12 +147,19 @@ describe('Client Side Encryption (Unified)', function () {
'rewrap from aws:name1 to aws:name2',
'can explicitly encrypt with a named KMS provider'
];
const dekExpirationTests = ['decrypt, wait, and decrypt again'];
if (delegatedKMIPTests.includes(description)) {
const shouldSkip = configuration.filters.ClientSideEncryptionFilter.filter({
metadata: { requires: { clientSideEncryption: '>=6.0.1' } }
});
if (typeof shouldSkip === 'string') return shouldSkip;
}
if (dekExpirationTests.includes(description)) {
const shouldSkip = configuration.filters.ClientSideEncryptionFilter.filter({
metadata: { requires: { clientSideEncryption: '>=6.4.0' } }
});
if (typeof shouldSkip === 'string') return shouldSkip;
}

return isServerless ? 'Unified CSFLE tests to not run on serverless' : false;
}
Expand Down
270 changes: 270 additions & 0 deletions test/spec/client-side-encryption/tests/legacy/keyCache.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,270 @@
{
"runOn": [
{
"minServerVersion": "4.1.10"
}
],
"database_name": "default",
"collection_name": "default",
"data": [],
"json_schema": {
"properties": {
"encrypted_w_altname": {
"encrypt": {
"keyId": "/altname",
"bsonType": "string",
"algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random"
}
},
"encrypted_string": {
"encrypt": {
"keyId": [
{
"$binary": {
"base64": "AAAAAAAAAAAAAAAAAAAAAA==",
"subType": "04"
}
}
],
"bsonType": "string",
"algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic"
}
},
"random": {
"encrypt": {
"keyId": [
{
"$binary": {
"base64": "AAAAAAAAAAAAAAAAAAAAAA==",
"subType": "04"
}
}
],
"bsonType": "string",
"algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random"
}
},
"encrypted_string_equivalent": {
"encrypt": {
"keyId": [
{
"$binary": {
"base64": "AAAAAAAAAAAAAAAAAAAAAA==",
"subType": "04"
}
}
],
"bsonType": "string",
"algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic"
}
}
},
"bsonType": "object"
},
"key_vault_data": [
{
"status": 1,
"_id": {
"$binary": {
"base64": "AAAAAAAAAAAAAAAAAAAAAA==",
"subType": "04"
}
},
"masterKey": {
"provider": "aws",
"key": "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0",
"region": "us-east-1"
},
"updateDate": {
"$date": {
"$numberLong": "1552949630483"
}
},
"keyMaterial": {
"$binary": {
"base64": "AQICAHhQNmWG2CzOm1dq3kWLM+iDUZhEqnhJwH9wZVpuZ94A8gEqnsxXlR51T5EbEVezUqqKAAAAwjCBvwYJKoZIhvcNAQcGoIGxMIGuAgEAMIGoBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDHa4jo6yp0Z18KgbUgIBEIB74sKxWtV8/YHje5lv5THTl0HIbhSwM6EqRlmBiFFatmEWaeMk4tO4xBX65eq670I5TWPSLMzpp8ncGHMmvHqRajNBnmFtbYxN3E3/WjxmdbOOe+OXpnGJPcGsftc7cB2shRfA4lICPnE26+oVNXT6p0Lo20nY5XC7jyCO",
"subType": "00"
}
},
"creationDate": {
"$date": {
"$numberLong": "1552949630483"
}
},
"keyAltNames": [
"altname",
"another_altname"
]
}
],
"tests": [
{
"description": "Insert with deterministic encryption, then find it",
"clientOptions": {
"autoEncryptOpts": {
"kmsProviders": {
"aws": {}
},
"keyExpirationMS": 1
}
},
"operations": [
{
"name": "insertOne",
"arguments": {
"document": {
"_id": 1,
"encrypted_string": "string0"
}
}
},
{
"name": "wait",
"object": "testRunner",
"arguments": {
"ms": 50
}
},
{
"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": "AAAAAAAAAAAAAAAAAAAAAA==",
"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": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==",
"subType": "06"
}
}
}
],
"ordered": true
},
"command_name": "insert"
}
},
{
"command_started_event": {
"command": {
"find": "default",
"filter": {
"_id": 1
}
},
"command_name": "find"
}
},
{
"command_started_event": {
"command": {
"find": "datakeys",
"filter": {
"$or": [
{
"_id": {
"$in": [
{
"$binary": {
"base64": "AAAAAAAAAAAAAAAAAAAAAA==",
"subType": "04"
}
}
]
}
},
{
"keyAltNames": {
"$in": []
}
}
]
},
"$db": "keyvault",
"readConcern": {
"level": "majority"
}
},
"command_name": "find"
}
}
],
"outcome": {
"collection": {
"data": [
{
"_id": 1,
"encrypted_string": {
"$binary": {
"base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==",
"subType": "06"
}
}
}
]
}
}
}
]
}
Loading