Skip to content

Commit 11a8060

Browse files
MONGOCRYPT-730 Add option to configure DEK cache lifetime (#900)
--------- Co-authored-by: Kevin Albertson <[email protected]>
1 parent 28e7472 commit 11a8060

10 files changed

+78
-23
lines changed

src/mongocrypt-cache-collinfo.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -61,5 +61,5 @@ void _mongocrypt_cache_collinfo_init(_mongocrypt_cache_t *cache) {
6161
cache->destroy_value = _destroy_value;
6262
_mongocrypt_mutex_init(&cache->mutex);
6363
cache->pair = NULL;
64-
cache->expiration = CACHE_EXPIRATION_MS;
64+
cache->expiration = CACHE_EXPIRATION_MS_DEFAULT;
6565
}

src/mongocrypt-cache-key.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ void _mongocrypt_cache_key_init(_mongocrypt_cache_t *cache) {
129129
cache->dump_attr = _dump_attr;
130130
_mongocrypt_mutex_init(&cache->mutex);
131131
cache->pair = NULL;
132-
cache->expiration = CACHE_EXPIRATION_MS;
132+
cache->expiration = CACHE_EXPIRATION_MS_DEFAULT;
133133
}
134134

135135
/* Since key cache may be looked up by either _id or keyAltName,

src/mongocrypt-cache-private.h

+2-2
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
#include "mongocrypt-mutex-private.h"
2121
#include "mongocrypt-status-private.h"
2222

23-
#define CACHE_EXPIRATION_MS 60000
23+
#define CACHE_EXPIRATION_MS_DEFAULT 60000
2424

2525
/* A generic simple cache.
2626
* To avoid overusing the names "key" or "id", the cache contains
@@ -74,4 +74,4 @@ void _mongocrypt_cache_set_expiration(_mongocrypt_cache_t *cache, uint64_t milli
7474

7575
uint32_t _mongocrypt_cache_num_entries(_mongocrypt_cache_t *cache);
7676

77-
#endif /* MONGOCRYPT_CACHE_PRIVATE */
77+
#endif /* MONGOCRYPT_CACHE_PRIVATE */

src/mongocrypt-cache.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ static bool _pair_expired(_mongocrypt_cache_t *cache, _mongocrypt_cache_pair_t *
2828
current = bson_get_monotonic_time() / 1000;
2929
BSON_ASSERT(current >= INT64_MIN + pair->last_updated);
3030
BSON_ASSERT(cache->expiration <= INT64_MAX);
31-
return (current - pair->last_updated) > (int64_t)cache->expiration;
31+
return cache->expiration > 0 && (current - pair->last_updated) > (int64_t)cache->expiration;
3232
}
3333

3434
/* Return the pair after the one being destroyed. */

src/mongocrypt.c

+13
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
* limitations under the License.
1515
*/
1616

17+
#include "mongocrypt.h"
1718
#include "mlib/error.h"
1819
#include "mlib/path.h"
1920
#include "mlib/thread.h"
@@ -24,6 +25,7 @@
2425
#include "mongocrypt-binary-private.h"
2526
#include "mongocrypt-cache-collinfo-private.h"
2627
#include "mongocrypt-cache-key-private.h"
28+
#include "mongocrypt-cache-private.h"
2729
#include "mongocrypt-config.h"
2830
#include "mongocrypt-crypto-private.h"
2931
#include "mongocrypt-log-private.h"
@@ -220,6 +222,17 @@ bool mongocrypt_setopt_kms_provider_aws(mongocrypt_t *crypt,
220222
return true;
221223
}
222224

225+
bool mongocrypt_setopt_key_expiration(mongocrypt_t *crypt, uint64_t cache_expiration_ms) {
226+
ASSERT_MONGOCRYPT_PARAM_UNINIT(crypt);
227+
if (cache_expiration_ms > INT64_MAX) {
228+
mongocrypt_status_t *status = crypt->status;
229+
CLIENT_ERR("expiration time must be less than %" PRId64 ", but got %" PRIu64, INT64_MAX, cache_expiration_ms);
230+
return false;
231+
}
232+
crypt->cache_key.expiration = cache_expiration_ms;
233+
return true;
234+
}
235+
223236
char *_mongocrypt_new_string_from_bytes(const void *in, int len) {
224237
const int max_bytes = 100;
225238
const int chars_per_byte = 2;

src/mongocrypt.h

+10
Original file line numberDiff line numberDiff line change
@@ -1519,6 +1519,16 @@ bool mongocrypt_ctx_setopt_query_type(mongocrypt_ctx_t *ctx, const char *query_t
15191519
MONGOCRYPT_EXPORT
15201520
bool mongocrypt_ctx_setopt_algorithm_range(mongocrypt_ctx_t *ctx, mongocrypt_binary_t *opts);
15211521

1522+
/**
1523+
* Set the expiration time for the data encryption key cache. Defaults to 60 seconds if not set.
1524+
*
1525+
* @param[in] ctx The @ref mongocrypt_ctx_t object.
1526+
* @param[in] cache_expiration_ms The cache expiration time in milliseconds. If zero, the cache
1527+
* never expires.
1528+
*/
1529+
MONGOCRYPT_EXPORT
1530+
bool mongocrypt_setopt_key_expiration(mongocrypt_t *crypt, uint64_t cache_expiration_ms);
1531+
15221532
/// String constants for setopt_query_type
15231533
#define MONGOCRYPT_QUERY_TYPE_EQUALITY_STR "equality"
15241534
// DEPRECATED: Support "rangePreview" has been removed in favor of "range".

test/test-mongocrypt-cache.c

-18
Original file line numberDiff line numberDiff line change
@@ -68,24 +68,6 @@ void _test_cache(_mongocrypt_tester_t *tester) {
6868
bson_destroy(entry2);
6969
}
7070

71-
static void _usleep(int64_t usec) {
72-
#ifdef _WIN32
73-
LARGE_INTEGER ft;
74-
HANDLE timer;
75-
76-
BSON_ASSERT(usec >= 0);
77-
78-
ft.QuadPart = -(10 * usec);
79-
timer = CreateWaitableTimer(NULL, true, NULL);
80-
SetWaitableTimer(timer, &ft, 0, NULL, NULL, 0);
81-
WaitForSingleObject(timer, INFINITE);
82-
CloseHandle(timer);
83-
#else
84-
BSON_ASSERT(usec >= 0);
85-
usleep((useconds_t)usec);
86-
#endif
87-
}
88-
8971
static void _test_cache_expiration(_mongocrypt_tester_t *tester) {
9072
_mongocrypt_cache_t cache;
9173
mongocrypt_status_t *status;

test/test-mongocrypt-ctx-encrypt.c

+25
Original file line numberDiff line numberDiff line change
@@ -756,6 +756,30 @@ static void _test_encrypt_caches_keys(_mongocrypt_tester_t *tester) {
756756
mongocrypt_destroy(crypt);
757757
}
758758

759+
static void _test_encrypt_cache_expiration(_mongocrypt_tester_t *tester) {
760+
mongocrypt_t *crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_WITH_SHORT_CACHE);
761+
mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);
762+
ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "test", -1, TEST_FILE("./test/example/cmd.json")), ctx);
763+
_mongocrypt_tester_run_ctx_to(tester, ctx, MONGOCRYPT_CTX_DONE);
764+
mongocrypt_ctx_destroy(ctx);
765+
766+
_usleep(2000);
767+
/* The next context requests keys again
768+
*/
769+
ctx = mongocrypt_ctx_new(crypt);
770+
ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "test", -1, TEST_FILE("./test/example/cmd.json")), ctx);
771+
_mongocrypt_tester_run_ctx_to(tester, ctx, MONGOCRYPT_CTX_NEED_MONGO_MARKINGS);
772+
ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TEST_FILE("./test/example/mongocryptd-reply.json")), ctx);
773+
ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
774+
BSON_ASSERT(mongocrypt_ctx_state(ctx) == MONGOCRYPT_CTX_NEED_MONGO_KEYS);
775+
ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TEST_FILE("./test/example/key-document.json")), ctx);
776+
ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
777+
_mongocrypt_tester_run_ctx_to(tester, ctx, MONGOCRYPT_CTX_DONE);
778+
779+
mongocrypt_ctx_destroy(ctx);
780+
mongocrypt_destroy(crypt);
781+
}
782+
759783
static void _test_encrypt_caches_keys_by_alt_name(_mongocrypt_tester_t *tester) {
760784
mongocrypt_t *crypt;
761785
mongocrypt_ctx_t *ctx;
@@ -5665,6 +5689,7 @@ void _mongocrypt_tester_install_ctx_encrypt(_mongocrypt_tester_t *tester) {
56655689
INSTALL_TEST(_test_local_schema);
56665690
INSTALL_TEST(_test_encrypt_caches_collinfo);
56675691
INSTALL_TEST(_test_encrypt_caches_keys);
5692+
INSTALL_TEST(_test_encrypt_cache_expiration);
56685693
INSTALL_TEST(_test_encrypt_caches_keys_by_alt_name);
56695694
INSTALL_TEST(_test_encrypt_random);
56705695
INSTALL_TEST(_test_encrypt_is_remote_schema);

test/test-mongocrypt.c

+21
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,24 @@ void _load_json_as_bson(const char *path, bson_t *out) {
6262
bson_json_reader_destroy(reader);
6363
}
6464

65+
void _usleep(int64_t usec) {
66+
#ifdef _WIN32
67+
LARGE_INTEGER ft;
68+
HANDLE timer;
69+
70+
BSON_ASSERT(usec >= 0);
71+
72+
ft.QuadPart = -(10 * usec);
73+
timer = CreateWaitableTimer(NULL, true, NULL);
74+
SetWaitableTimer(timer, &ft, 0, NULL, NULL, 0);
75+
WaitForSingleObject(timer, INFINITE);
76+
CloseHandle(timer);
77+
#else
78+
BSON_ASSERT(usec >= 0);
79+
usleep((useconds_t)usec);
80+
#endif
81+
}
82+
6583
#define TEST_DATA_COUNT_INC(var) \
6684
(var)++; \
6785
if ((var) >= TEST_DATA_COUNT) { \
@@ -501,6 +519,9 @@ mongocrypt_t *_mongocrypt_tester_mongocrypt(tester_mongocrypt_flags flags) {
501519
} else {
502520
crypt->opts.use_range_v2 = false;
503521
}
522+
if (flags & TESTER_MONGOCRYPT_WITH_SHORT_CACHE) {
523+
ASSERT(mongocrypt_setopt_key_expiration(crypt, 1));
524+
}
504525
ASSERT_OK(mongocrypt_init(crypt), crypt);
505526
if (flags & TESTER_MONGOCRYPT_WITH_CRYPT_SHARED_LIB) {
506527
if (mongocrypt_crypt_shared_lib_version(crypt) == 0) {

test/test-mongocrypt.h

+4
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ typedef enum tester_mongocrypt_flags {
4444
TESTER_MONGOCRYPT_WITH_CRYPT_V1 = 1 << 1,
4545
/// Enable range V2
4646
TESTER_MONGOCRYPT_WITH_RANGE_V2 = 1 << 2,
47+
/// Short cache expiration
48+
TESTER_MONGOCRYPT_WITH_SHORT_CACHE = 1 << 3,
4749
} tester_mongocrypt_flags;
4850

4951
/* Arbitrary max of 2048 instances of temporary test data. Increase as needed.
@@ -245,4 +247,6 @@ void _test_ctx_wrap_and_feed_key(mongocrypt_ctx_t *ctx,
245247
_mongocrypt_buffer_t *key,
246248
mongocrypt_status_t *status);
247249

250+
void _usleep(int64_t usec);
251+
248252
#endif

0 commit comments

Comments
 (0)