Skip to content

Commit 5829cca

Browse files
authored
CSHARP-5205: Add option to configure DEK cache lifetime (#1614)
1 parent f78ad53 commit 5829cca

File tree

11 files changed

+420
-8
lines changed

11 files changed

+420
-8
lines changed
Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
{
2+
"description": "keyCache-explicit",
3+
"schemaVersion": "1.22",
4+
"runOnRequirements": [
5+
{
6+
"csfle": true
7+
}
8+
],
9+
"createEntities": [
10+
{
11+
"client": {
12+
"id": "client0",
13+
"observeEvents": [
14+
"commandStartedEvent"
15+
]
16+
}
17+
},
18+
{
19+
"clientEncryption": {
20+
"id": "clientEncryption0",
21+
"clientEncryptionOpts": {
22+
"keyVaultClient": "client0",
23+
"keyVaultNamespace": "keyvault.datakeys",
24+
"kmsProviders": {
25+
"local": {
26+
"key": "OCTP9uKPPmvuqpHlqq83gPk4U6rUPxKVRRyVtrjFmVjdoa4Xzm1SzUbr7aIhNI42czkUBmrCtZKF31eaaJnxEBkqf0RFukA9Mo3NEHQWgAQ2cn9duOcRbaFUQo2z0/rB"
27+
}
28+
},
29+
"keyExpirationMS": 1
30+
}
31+
}
32+
},
33+
{
34+
"database": {
35+
"id": "database0",
36+
"client": "client0",
37+
"databaseName": "keyvault"
38+
}
39+
},
40+
{
41+
"collection": {
42+
"id": "collection0",
43+
"database": "database0",
44+
"collectionName": "datakeys"
45+
}
46+
}
47+
],
48+
"initialData": [
49+
{
50+
"databaseName": "keyvault",
51+
"collectionName": "datakeys",
52+
"documents": [
53+
{
54+
"_id": {
55+
"$binary": {
56+
"base64": "a+YWzdygTAG62/cNUkqZiQ==",
57+
"subType": "04"
58+
}
59+
},
60+
"keyAltNames": [],
61+
"keyMaterial": {
62+
"$binary": {
63+
"base64": "iocBkhO3YBokiJ+FtxDTS71/qKXQ7tSWhWbcnFTXBcMjarsepvALeJ5li+SdUd9ePuatjidxAdMo7vh1V2ZESLMkQWdpPJ9PaJjA67gKQKbbbB4Ik5F2uKjULvrMBnFNVRMup4JNUwWFQJpqbfMveXnUVcD06+pUpAkml/f+DSXrV3e5rxciiNVtz03dAG8wJrsKsFXWj6vTjFhsfknyBA==",
64+
"subType": "00"
65+
}
66+
},
67+
"creationDate": {
68+
"$date": {
69+
"$numberLong": "1552949630483"
70+
}
71+
},
72+
"updateDate": {
73+
"$date": {
74+
"$numberLong": "1552949630483"
75+
}
76+
},
77+
"status": {
78+
"$numberInt": "0"
79+
},
80+
"masterKey": {
81+
"provider": "local"
82+
}
83+
}
84+
]
85+
}
86+
],
87+
"tests": [
88+
{
89+
"description": "decrypt, wait, and decrypt again",
90+
"operations": [
91+
{
92+
"name": "decrypt",
93+
"object": "clientEncryption0",
94+
"arguments": {
95+
"value": {
96+
"$binary": {
97+
"base64": "AWvmFs3coEwButv3DVJKmYkCJ6lUzRX9R28WNlw5uyndb+8gurA+p8q14s7GZ04K2ZvghieRlAr5UwZbow3PMq27u5EIhDDczwBFcbdP1amllw==",
98+
"subType": "06"
99+
}
100+
}
101+
},
102+
"expectResult": "foobar"
103+
},
104+
{
105+
"name": "wait",
106+
"object": "testRunner",
107+
"arguments": {
108+
"ms": 50
109+
}
110+
},
111+
{
112+
"name": "decrypt",
113+
"object": "clientEncryption0",
114+
"arguments": {
115+
"value": {
116+
"$binary": {
117+
"base64": "AWvmFs3coEwButv3DVJKmYkCJ6lUzRX9R28WNlw5uyndb+8gurA+p8q14s7GZ04K2ZvghieRlAr5UwZbow3PMq27u5EIhDDczwBFcbdP1amllw==",
118+
"subType": "06"
119+
}
120+
}
121+
},
122+
"expectResult": "foobar"
123+
}
124+
],
125+
"expectEvents": [
126+
{
127+
"client": "client0",
128+
"events": [
129+
{
130+
"commandStartedEvent": {
131+
"command": {
132+
"find": "datakeys",
133+
"filter": {
134+
"$or": [
135+
{
136+
"_id": {
137+
"$in": [
138+
{
139+
"$binary": {
140+
"base64": "a+YWzdygTAG62/cNUkqZiQ==",
141+
"subType": "04"
142+
}
143+
}
144+
]
145+
}
146+
},
147+
{
148+
"keyAltNames": {
149+
"$in": []
150+
}
151+
}
152+
]
153+
},
154+
"$db": "keyvault",
155+
"readConcern": {
156+
"level": "majority"
157+
}
158+
}
159+
}
160+
},
161+
{
162+
"commandStartedEvent": {
163+
"command": {
164+
"find": "datakeys",
165+
"filter": {
166+
"$or": [
167+
{
168+
"_id": {
169+
"$in": [
170+
{
171+
"$binary": {
172+
"base64": "a+YWzdygTAG62/cNUkqZiQ==",
173+
"subType": "04"
174+
}
175+
}
176+
]
177+
}
178+
},
179+
{
180+
"keyAltNames": {
181+
"$in": []
182+
}
183+
}
184+
]
185+
},
186+
"$db": "keyvault",
187+
"readConcern": {
188+
"level": "majority"
189+
}
190+
}
191+
}
192+
}
193+
]
194+
}
195+
]
196+
}
197+
]
198+
}
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
description: keyCache-explicit
2+
3+
schemaVersion: "1.22"
4+
5+
runOnRequirements:
6+
- csfle: true
7+
8+
createEntities:
9+
- client:
10+
id: &client0 client0
11+
observeEvents:
12+
- commandStartedEvent
13+
- clientEncryption:
14+
id: &clientEncryption0 clientEncryption0
15+
clientEncryptionOpts:
16+
keyVaultClient: *client0
17+
keyVaultNamespace: keyvault.datakeys
18+
kmsProviders:
19+
"local" : { key: "OCTP9uKPPmvuqpHlqq83gPk4U6rUPxKVRRyVtrjFmVjdoa4Xzm1SzUbr7aIhNI42czkUBmrCtZKF31eaaJnxEBkqf0RFukA9Mo3NEHQWgAQ2cn9duOcRbaFUQo2z0/rB" }
20+
keyExpirationMS: 1
21+
- database:
22+
id: &database0 database0
23+
client: *client0
24+
databaseName: &database0Name keyvault
25+
- collection:
26+
id: &collection0 collection0
27+
database: *database0
28+
collectionName: &collection0Name datakeys
29+
30+
initialData:
31+
- databaseName: *database0Name
32+
collectionName: *collection0Name
33+
documents:
34+
- {
35+
"_id": {
36+
"$binary": {
37+
"base64": "a+YWzdygTAG62/cNUkqZiQ==",
38+
"subType": "04"
39+
}
40+
},
41+
"keyAltNames": [],
42+
"keyMaterial": {
43+
"$binary": {
44+
"base64": "iocBkhO3YBokiJ+FtxDTS71/qKXQ7tSWhWbcnFTXBcMjarsepvALeJ5li+SdUd9ePuatjidxAdMo7vh1V2ZESLMkQWdpPJ9PaJjA67gKQKbbbB4Ik5F2uKjULvrMBnFNVRMup4JNUwWFQJpqbfMveXnUVcD06+pUpAkml/f+DSXrV3e5rxciiNVtz03dAG8wJrsKsFXWj6vTjFhsfknyBA==",
45+
"subType": "00"
46+
}
47+
},
48+
"creationDate": {"$date": {"$numberLong": "1552949630483"}},
49+
"updateDate": {"$date": {"$numberLong": "1552949630483"}},
50+
"status": {"$numberInt": "0"},
51+
"masterKey": {"provider": "local"}
52+
}
53+
54+
tests:
55+
- description: decrypt, wait, and decrypt again
56+
operations:
57+
- name: decrypt
58+
object: *clientEncryption0
59+
arguments:
60+
value: { "$binary" : { "base64" : "AWvmFs3coEwButv3DVJKmYkCJ6lUzRX9R28WNlw5uyndb+8gurA+p8q14s7GZ04K2ZvghieRlAr5UwZbow3PMq27u5EIhDDczwBFcbdP1amllw==", "subType" : "06" } }
61+
expectResult: "foobar"
62+
- name: wait
63+
object: testRunner
64+
arguments:
65+
ms: 50 # Wait long enough to account for coarse time resolution on Windows (CDRIVER-4526).
66+
- name: decrypt
67+
object: *clientEncryption0
68+
arguments:
69+
value: { "$binary" : { "base64" : "AWvmFs3coEwButv3DVJKmYkCJ6lUzRX9R28WNlw5uyndb+8gurA+p8q14s7GZ04K2ZvghieRlAr5UwZbow3PMq27u5EIhDDczwBFcbdP1amllw==", "subType" : "06" } }
70+
expectResult: "foobar"
71+
expectEvents:
72+
- client: *client0
73+
events:
74+
- commandStartedEvent:
75+
command:
76+
find: datakeys
77+
filter: {"$or": [{"_id": {"$in": [ {'$binary': {'base64': 'a+YWzdygTAG62/cNUkqZiQ==', 'subType': '04'}} ] }}, {"keyAltNames": {"$in": []}}]}
78+
$db: keyvault
79+
readConcern: { level: "majority" }
80+
- commandStartedEvent:
81+
command:
82+
find: datakeys
83+
filter: {"$or": [{"_id": {"$in": [ {'$binary': {'base64': 'a+YWzdygTAG62/cNUkqZiQ==', 'subType': '04'}} ] }}, {"keyAltNames": {"$in": []}}]}
84+
$db: keyvault
85+
readConcern: { level: "majority" }

src/MongoDB.Driver.Encryption/ClientEncryption.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,8 @@ public ClientEncryption(ClientEncryptionOptions clientEncryptionOptions)
4848
encryptedFieldsMap: null,
4949
isCryptSharedLibRequired: null,
5050
kmsProviders: clientEncryptionOptions.KmsProviders,
51-
schemaMap: null);
51+
schemaMap: null,
52+
keyExpiration: clientEncryptionOptions.KeyExpiration);
5253

5354
_cryptClient = CryptClientFactory.Create(cryptClientSettings);
5455

src/MongoDB.Driver.Encryption/ClientEncryptionOptions.cs

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ namespace MongoDB.Driver.Encryption
2525
public sealed class ClientEncryptionOptions
2626
{
2727
// private fields
28+
private TimeSpan? _keyExpiration;
2829
private readonly IMongoClient _keyVaultClient;
2930
private readonly CollectionNamespace _keyVaultNamespace;
3031
private readonly IReadOnlyDictionary<string, IReadOnlyDictionary<string, object>> _kmsProviders;
@@ -43,17 +44,34 @@ public ClientEncryptionOptions(
4344
CollectionNamespace keyVaultNamespace,
4445
IReadOnlyDictionary<string, IReadOnlyDictionary<string, object>> kmsProviders,
4546
Optional<IReadOnlyDictionary<string, SslSettings>> tlsOptions = default)
47+
: this(keyVaultClient, keyVaultNamespace, kmsProviders, tlsOptions, keyExpiration: null)
48+
{
49+
}
50+
51+
private ClientEncryptionOptions(
52+
IMongoClient keyVaultClient,
53+
CollectionNamespace keyVaultNamespace,
54+
IReadOnlyDictionary<string, IReadOnlyDictionary<string, object>> kmsProviders,
55+
Optional<IReadOnlyDictionary<string, SslSettings>> tlsOptions = default,
56+
Optional<TimeSpan?> keyExpiration = default)
4657
{
4758
_keyVaultClient = Ensure.IsNotNull(keyVaultClient, nameof(keyVaultClient));
4859
_keyVaultNamespace = Ensure.IsNotNull(keyVaultNamespace, nameof(keyVaultNamespace));
4960
_kmsProviders = Ensure.IsNotNull(kmsProviders, nameof(kmsProviders));
5061
_tlsOptions = tlsOptions.WithDefault(new Dictionary<string, SslSettings>());
62+
_keyExpiration = keyExpiration.WithDefault(null);
5163

5264
EnsureKmsProvidersAreValid(_kmsProviders);
5365
EnsureKmsProvidersTlsSettingsAreValid(_tlsOptions);
5466
}
5567

5668
// public properties
69+
70+
/// <summary>
71+
/// Gets the data encryption key cache expiration time.
72+
/// </summary>
73+
public TimeSpan? KeyExpiration => _keyExpiration;
74+
5775
/// <summary>
5876
/// Gets the key vault client.
5977
/// </summary>
@@ -104,7 +122,18 @@ public ClientEncryptionOptions With(
104122
keyVaultClient: keyVaultClient.WithDefault(_keyVaultClient),
105123
keyVaultNamespace: keyVaultNamespace.WithDefault(_keyVaultNamespace),
106124
kmsProviders: kmsProviders.WithDefault(_kmsProviders),
107-
tlsOptions: Optional.Create(tlsOptions.WithDefault(_tlsOptions)));
125+
tlsOptions: Optional.Create(tlsOptions.WithDefault(_tlsOptions)),
126+
keyExpiration: _keyExpiration);
127+
}
128+
129+
/// <summary>
130+
/// Sets the data encryption key cache expiration time. If not set, it defaults to 60 seconds.
131+
/// If set to TimeSpan.Zero, the cache never expires.
132+
/// </summary>
133+
/// <param name="keyExpiration">The data encryption key cache expiration time.</param>
134+
public void SetKeyExpiration(TimeSpan? keyExpiration)
135+
{
136+
_keyExpiration = keyExpiration;
108137
}
109138

110139
private static void EnsureKmsProvidersAreValid(IReadOnlyDictionary<string, IReadOnlyDictionary<string, object>> kmsProviders)

src/MongoDB.Driver.Encryption/CryptClientFactory.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,8 @@ public static CryptClient Create(CryptClientSettings cryptClientSettings)
8282
bypassQueryAnalysis: cryptClientSettings.BypassQueryAnalysis.GetValueOrDefault(false),
8383
cryptClientSettings.CryptSharedLibPath,
8484
cryptClientSettings.CryptSharedLibSearchPath,
85-
cryptClientSettings.IsCryptSharedLibRequired ?? false);
85+
cryptClientSettings.IsCryptSharedLibRequired ?? false,
86+
(long?)cryptClientSettings.KeyExpiration?.TotalMilliseconds);
8687

8788
return Create(cryptOptions);
8889
}
@@ -161,6 +162,11 @@ public static CryptClient Create(CryptOptions options)
161162
Library.mongocrypt_setopt_append_crypt_shared_lib_search_path(handle, options.CryptSharedLibSearchPath);
162163
}
163164

165+
if (options.KeyExpirationMs != null)
166+
{
167+
Library.mongocrypt_setopt_key_expiration(handle, (ulong)options.KeyExpirationMs.Value);
168+
}
169+
164170
Library.mongocrypt_setopt_use_need_kms_credentials_state(handle);
165171
Library.mongocrypt_setopt_enable_multiple_collinfo(handle);
166172
Library.mongocrypt_setopt_retry_kms(handle, true);

0 commit comments

Comments
 (0)