Skip to content
Open
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
11 changes: 0 additions & 11 deletions src/MongoDB.Bson/ObjectModel/BsonDocument.cs
Original file line number Diff line number Diff line change
Expand Up @@ -123,17 +123,6 @@ public BsonDocument(IEnumerable<BsonElement> elements)
AddRange(elements);
}

/// <summary>
/// Initializes a new instance of the BsonDocument class and adds one or more elements.
/// </summary>
/// <param name="elements">One or more elements to add to the document.</param>
[Obsolete("Use BsonDocument(IEnumerable<BsonElement> elements) instead.")]
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public BsonDocument(params BsonElement[] elements)
{
AddRange(elements);
}

/// <summary>
/// Initializes a new instance of the BsonDocument class and creates and adds a new element.
/// </summary>
Expand Down
8 changes: 4 additions & 4 deletions src/MongoDB.Driver.Encryption/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ This project wraps **libmongocrypt** (the C library that implements CSFLE and Qu

- `ClientEncryption` — explicit encryption surface, incl. `CreateDataKey`, `RewrapManyDataKey`, `Encrypt`, `EncryptExpression`, `Decrypt`, `GetKey`, `GetKeyByAlternateKeyName`, `AddAlternateKeyName`, `RemoveAlternateKeyName`, `DeleteKey`, `GetKeys`, `CreateEncryptedCollection`. All have sync + async pairs and accept a `CancellationToken`. Backed by `ExplicitEncryptionLibMongoCryptController`.
- `ClientEncryptionOptions` — `KeyVaultClient` (typically a separate `IMongoClient` for the key vault), `KeyVaultNamespace`, `KmsProviders` (per-provider credentials), `TlsOptions`, `KeyExpiration` (DEK cache TTL; the C# property defaults to `null`, which causes libmongocrypt to apply its 60-second default; `Zero` = never expire). Validates KMS option values are `byte[]` or `string`; rejects per-provider TLS settings that supply a `ServerCertificateValidationCallback` (insecure-by-construction). Other `SslSettings` knobs (custom CAs via the standard validation callback chain, client certificates, etc.) are not blocked.
- `EncryptOptions`, `EncryptionAlgorithm`, `DataKeyOptions`, `RewrapManyDataKeyOptions`, `RangeOptions`, `TextOptions` (with `PrefixOptions`, `SubstringOptions`, `SuffixOptions` for the QE TextPreview surface), `CsfleSchemaBuilder` (a fluent builder for **CSFLE** `$jsonSchema`-style schemas suitable for `AutoEncryptionOptions.SchemaMap`; QE encrypted-field schemas are configured via `AutoEncryptionOptions.EncryptedFieldsMap`, not via this builder).
- `EncryptOptions`, `EncryptionAlgorithm`, `DataKeyOptions`, `RewrapManyDataKeyOptions`, `RangeOptions`, `StringOptions` (with `PrefixOptions`, `SubstringOptions`, `SuffixOptions` for the QE String surface), `CsfleSchemaBuilder` (a fluent builder for **CSFLE** `$jsonSchema`-style schemas suitable for `AutoEncryptionOptions.SchemaMap`; QE encrypted-field schemas are configured via `AutoEncryptionOptions.EncryptedFieldsMap`, not via this builder).

## Controllers

Expand Down Expand Up @@ -49,13 +49,13 @@ libmongocrypt asks managed code for AES / HMAC / random / RSA signing via callba
- `CsfleSchemaBuilder` — fluent builder for **CSFLE** `$jsonSchema`-style schemas (composes with `AutoEncryptionOptions.SchemaMap`). The duplicate-namespace check is incidental: `Encrypt<T>(CollectionNamespace, Action<EncryptedCollectionBuilder<T>>)` calls `_schemas.Add(...)` on the underlying `Dictionary<string, BsonDocument>`, which throws `ArgumentException` on a duplicate key — there is no explicit validation step beyond that. **Not** the entry point for Queryable Encryption — QE schemas are configured via `AutoEncryptionOptions.EncryptedFieldsMap`.
- `EncryptionAlgorithm` is a single flat enum — values are not partitioned in the type system, only by usage convention:
- **CSFLE-only by convention** — `AEAD_AES_256_CBC_HMAC_SHA_512_Deterministic` (equality-queryable, same plaintext → same ciphertext), `AEAD_AES_256_CBC_HMAC_SHA_512_Random` (no queries possible).
- **QE-only by convention** — `Indexed` (equality with contention), `Range` (range queries), `TextPreview` (preview), `Unindexed`. Server-version availability (preview vs GA) for each algorithm is a server-side concern; consult the MongoDB server release notes rather than relying on driver-side enum metadata. The "Preview" suffix on `TextPreview` reflects the server's preview status — but `EncryptionAlgorithm` is a **public enum**, so the value itself is SemVer-covered: renaming or removing `TextPreview` (e.g. once the server feature GAs as `Text`) requires an `[Obsolete]` deprecation cycle, not an in-place rename. The migration shape is additive-then-deprecate: introduce a new `Text` enum member alongside `TextPreview`, mark `TextPreview` `[Obsolete]`, and only remove it in a later major version — never reuse the existing enum value, since the integer is part of the on-the-wire contract for any caller that has it baked in.
- **QE-only by convention** — `Indexed` (equality with contention), `Range` (range queries), `String` (prefix / substring / suffix queries, including the `*Preview` query types), `Unindexed`. Server-version availability (preview vs GA) for each algorithm is a server-side concern; consult the MongoDB server release notes rather than relying on driver-side enum metadata. Preview versus GA is expressed by the **query type** (`prefixPreview` vs `prefix`, and so on — see `ValidStringQueryTypes` in `EncryptOptions.cs`), not by a separate algorithm or options type. `EncryptionAlgorithm` is a **public enum**, so renaming or removing a member is SemVer-covered and needs an `[Obsolete]` cycle followed by removal in a major version, not an in-place rename. The integers are **not** part of the wire contract — `ConvertEnumAlgorithmToString` sends the member name as a string, and the enum declares no explicit values — so members may be removed without pinning the remaining ordinals. Contrast `CompressorType` in `Core/Compression/ICompressor.cs`, which does pin its values because there the integers are wire protocol IDs.
Server-side enforcement decides which value is valid in a given context. Confusing CSFLE and QE algorithms is a recurring bug.

## Mongocryptd vs crypt_shared

- `MongocryptdFactory` — spawns a local `mongocryptd` process if needed. Default URI `mongodb://localhost:27020`. Controlled by `extraOptions["mongocryptdURI"]`, `mongocryptdSpawnArgs`. Skipped if `BypassQueryAnalysis = true`.
- **`crypt_shared` is preferred** — it's a shared library loaded by libmongocrypt, no separate process. Set `CRYPT_SHARED_LIB_PATH` to point libmongocrypt at it. **QE (Indexed/Range/TextPreview) requires `crypt_shared`**; mongocryptd does not implement QE.
- **`crypt_shared` is preferred** — it's a shared library loaded by libmongocrypt, no separate process. Set `CRYPT_SHARED_LIB_PATH` to point libmongocrypt at it. **QE (Indexed/Range/String) requires `crypt_shared`**; mongocryptd does not implement QE.

## Threading & lifecycle

Expand All @@ -70,7 +70,7 @@ libmongocrypt asks managed code for AES / HMAC / random / RSA signing via callba
- **State-machine misuse.** Calling `Encrypt` / `Decrypt` before `InitContext`, or feeding wrong-shape input, corrupts the context. Always drive contexts to `DONE`.
- **KMS credential expiry mid-operation.** AWS STS, Azure IMDS, GCP service tokens can expire. libmongocrypt asks for fresh credentials via `NEED_KMS_CREDENTIALS`; the controller must refetch and resupply. Failing to handle this looks like sporadic "auth failed" errors under load.
- **DEK cache staleness.** `KeyExpiration` is the cache-pruning lever — TTL expiry evicts a DEK on next lookup, not in the background. Long-lived processes that never re-encrypt may accumulate cache entries. `RewrapManyDataKey` is **key rotation** (re-encrypts each DEK with a new KEK in the key vault); it does not itself prune the local DEK cache, but is the canonical way to rotate keys on a schedule — entries then expire normally per `KeyExpiration`.
- **CSFLE vs QE algorithms confused.** `Deterministic`/`Random` are CSFLE-only; `Indexed`/`Range`/`TextPreview`/`Unindexed` are QE-only. The wrong combination on the server side fails with cryptic schema errors.
- **CSFLE vs QE algorithms confused.** `Deterministic`/`Random` are CSFLE-only; `Indexed`/`Range`/`String`/`Unindexed` are QE-only. The wrong combination on the server side fails with cryptic schema errors.
- **SafeHandle ordering bug.** A `ContextSafeHandle` outliving its parent `MongoCryptSafeHandle` dereferences a destroyed pointer. Don't rearrange disposal order without checking `GC.KeepAlive` calls.
- **TLS callback security.** `ClientEncryptionOptions` rejects insecure TLS callbacks at construction. Don't add a "for testing" bypass that disables this — tests should use the mock KMS instead.

Expand Down
42 changes: 0 additions & 42 deletions src/MongoDB.Driver.Encryption/ClientEncryption.cs
Original file line number Diff line number Diff line change
Expand Up @@ -79,27 +79,6 @@ public BsonDocument AddAlternateKeyName(Guid id, string alternateKeyName, Cancel
public Task<BsonDocument> AddAlternateKeyNameAsync(Guid id, string alternateKeyName, CancellationToken cancellationToken = default) =>
_libMongoCryptController.AddAlternateKeyNameAsync(id, alternateKeyName, cancellationToken);

/// <summary>
/// Create encrypted collection.
/// </summary>
/// <param name="database">The database.</param>
/// <param name="collectionName">The collection name.</param>
/// <param name="createCollectionOptions">The create collection options.</param>
/// <param name="kmsProvider">The kms provider.</param>
/// <param name="dataKeyOptions">The datakey options.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>The operation result.</returns>
/// <remarks>
/// If EncryptionFields contains a keyId with a null value, a data key will be automatically generated and returned in <see cref="CreateEncryptedCollectionResult.EncryptedFields"/>.
/// </remarks>
[Obsolete("Use the overload with masterKey instead.")]
public CreateEncryptedCollectionResult CreateEncryptedCollection(IMongoDatabase database, string collectionName, CreateCollectionOptions createCollectionOptions, string kmsProvider, DataKeyOptions dataKeyOptions, CancellationToken cancellationToken = default)
{
Ensure.That(dataKeyOptions?.AlternateKeyNames == null && dataKeyOptions?.KeyMaterial == null, $"{nameof(CreateEncryptedCollection)} supports only {nameof(dataKeyOptions.MasterKey)} in {nameof(DataKeyOptions)}.");

return CreateEncryptedCollection(database, collectionName, createCollectionOptions, kmsProvider, dataKeyOptions?.MasterKey, cancellationToken);
}

/// <summary>
/// Create encrypted collection.
/// </summary>
Expand Down Expand Up @@ -142,27 +121,6 @@ public CreateEncryptedCollectionResult CreateEncryptedCollection(IMongoDatabase
return new CreateEncryptedCollectionResult(encryptedFields);
}

/// <summary>
/// Create encrypted collection.
/// </summary>
/// <param name="database">The database.</param>
/// <param name="collectionName">The collection name.</param>
/// <param name="createCollectionOptions">The create collection options.</param>
/// <param name="kmsProvider">The kms provider.</param>
/// <param name="dataKeyOptions">The datakey options.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>The operation result.</returns>
/// <remarks>
/// If EncryptionFields contains a keyId with a null value, a data key will be automatically generated and returned in <see cref="CreateEncryptedCollectionResult.EncryptedFields"/>.
/// </remarks>
[Obsolete("Use the overload with masterKey instead.")]
public Task<CreateEncryptedCollectionResult> CreateEncryptedCollectionAsync(IMongoDatabase database, string collectionName, CreateCollectionOptions createCollectionOptions, string kmsProvider, DataKeyOptions dataKeyOptions, CancellationToken cancellationToken = default)
{
Ensure.That(dataKeyOptions?.AlternateKeyNames == null && dataKeyOptions?.KeyMaterial == null, $"{nameof(CreateEncryptedCollection)} supports only {nameof(dataKeyOptions.MasterKey)} in {nameof(DataKeyOptions)}.");

return CreateEncryptedCollectionAsync(database, collectionName, createCollectionOptions, kmsProvider, dataKeyOptions?.MasterKey, cancellationToken);
}

/// <summary>
/// Create encrypted collection.
/// </summary>
Expand Down
Loading