From 1a94335cd3e15ea6dcb4ca575b7b482e02613e37 Mon Sep 17 00:00:00 2001
From: Adelin Owona <51498470+adelinowona@users.noreply.github.com>
Date: Thu, 23 May 2024 10:53:54 -0400
Subject: [PATCH] CSHARP-4944: Enable use of native crypto in libmongocrypt
bindings (#796)
---
.../CryptClientFactory.cs | 12 ++--
bindings/cs/MongoDB.Libmongocrypt/Library.cs | 10 +++
.../cs/MongoDB.Libmongocrypt/LibraryLoader.cs | 66 +++++++++----------
bindings/cs/README.md | 3 +
bindings/cs/Scripts/build.cake | 2 +-
5 files changed, 54 insertions(+), 39 deletions(-)
diff --git a/bindings/cs/MongoDB.Libmongocrypt/CryptClientFactory.cs b/bindings/cs/MongoDB.Libmongocrypt/CryptClientFactory.cs
index 46d249546..fb2ebb943 100644
--- a/bindings/cs/MongoDB.Libmongocrypt/CryptClientFactory.cs
+++ b/bindings/cs/MongoDB.Libmongocrypt/CryptClientFactory.cs
@@ -15,6 +15,7 @@
*/
using System;
+using System.Linq;
namespace MongoDB.Libmongocrypt
{
@@ -34,6 +35,9 @@ public class CryptClientFactory
private static Library.Delegates.RandomCallback __randomCallback = new Library.Delegates.RandomCallback(SecureRandomCallback.GenerateRandom);
private static Library.Delegates.CryptoHmacCallback __signRsaesPkcs1HmacCallback = new Library.Delegates.CryptoHmacCallback(SigningRSAESPKCSCallback.RsaSign);
+ // mongocrypt_is_crypto_available is only available in libmongocrypt version >= 1.9
+ private static readonly Version __mongocryptIsCryptoAvailableMinVersion = Version.Parse("1.9");
+
/// Creates a CryptClient with the specified options.
/// The options.
/// A CryptClient
@@ -42,14 +46,14 @@ public static CryptClient Create(CryptOptions options)
MongoCryptSafeHandle handle = null;
Status status = null;
+ var cryptoAvailable = Version.Parse(Library.Version.Split('-', '+').First()) >= __mongocryptIsCryptoAvailableMinVersion && Library.mongocrypt_is_crypto_available();
+
try
{
handle = Library.mongocrypt_new();
status = new Status();
-
- // The below code can be avoided on Windows. So, we don't call it on this system
- // to avoid restrictions on target frameworks that present in some of below
- if (OperatingSystemHelper.CurrentOperatingSystem != OperatingSystemPlatform.Windows)
+
+ if (!cryptoAvailable)
{
handle.Check(
status,
diff --git a/bindings/cs/MongoDB.Libmongocrypt/Library.cs b/bindings/cs/MongoDB.Libmongocrypt/Library.cs
index b77035cdf..13d1babc1 100644
--- a/bindings/cs/MongoDB.Libmongocrypt/Library.cs
+++ b/bindings/cs/MongoDB.Libmongocrypt/Library.cs
@@ -48,6 +48,9 @@ static Library()
_mongocrypt_ctx_setopt_key_encryption_key = new Lazy(
() => __loader.Value.GetFunction(
("mongocrypt_ctx_setopt_key_encryption_key")), true);
+
+ _mongocrypt_is_crypto_available = new Lazy(
+ () => __loader.Value.GetFunction("mongocrypt_is_crypto_available"), true);
_mongocrypt_setopt_aes_256_ecb = new Lazy(
() => __loader.Value.GetFunction(
@@ -229,6 +232,8 @@ public static string Version
internal static Delegates.mongocrypt_setopt_log_handler mongocrypt_setopt_log_handler => _mongocrypt_setopt_log_handler.Value;
internal static Delegates.mongocrypt_setopt_kms_providers mongocrypt_setopt_kms_providers => _mongocrypt_setopt_kms_providers.Value;
internal static Delegates.mongocrypt_ctx_setopt_key_encryption_key mongocrypt_ctx_setopt_key_encryption_key => _mongocrypt_ctx_setopt_key_encryption_key.Value;
+
+ internal static Delegates.mongocrypt_is_crypto_available mongocrypt_is_crypto_available => _mongocrypt_is_crypto_available.Value;
internal static Delegates.mongocrypt_setopt_aes_256_ecb mongocrypt_setopt_aes_256_ecb => _mongocrypt_setopt_aes_256_ecb.Value;
internal static Delegates.mongocrypt_setopt_bypass_query_analysis mongocrypt_setopt_bypass_query_analysis => _mongocrypt_setopt_bypass_query_analysis.Value;
@@ -306,6 +311,8 @@ public static string Version
private static readonly Lazy _mongocrypt_setopt_kms_providers;
private static readonly Lazy _mongocrypt_ctx_setopt_key_encryption_key;
+
+ private static readonly Lazy _mongocrypt_is_crypto_available;
private static readonly Lazy _mongocrypt_setopt_aes_256_ecb;
private static readonly Lazy _mongocrypt_setopt_bypass_query_analysis;
@@ -399,6 +406,9 @@ internal class Delegates
public delegate MongoCryptSafeHandle mongocrypt_new();
+ [return: MarshalAs(UnmanagedType.I1)]
+ public delegate bool mongocrypt_is_crypto_available();
+
public delegate void LogCallback([MarshalAs(UnmanagedType.I4)] LogLevel level, IntPtr messasge,
uint message_length, IntPtr context);
diff --git a/bindings/cs/MongoDB.Libmongocrypt/LibraryLoader.cs b/bindings/cs/MongoDB.Libmongocrypt/LibraryLoader.cs
index 5390e2a4b..6bbc0d741 100644
--- a/bindings/cs/MongoDB.Libmongocrypt/LibraryLoader.cs
+++ b/bindings/cs/MongoDB.Libmongocrypt/LibraryLoader.cs
@@ -28,6 +28,7 @@ namespace MongoDB.Libmongocrypt
internal class LibraryLoader
{
private ISharedLibraryLoader _loader;
+ private static readonly string __libmongocryptLibPath = Environment.GetEnvironmentVariable("LIBMONGOCRYPT_PATH");
public LibraryLoader()
{
@@ -54,40 +55,13 @@ public LibraryLoader()
switch (OperatingSystemHelper.CurrentOperatingSystem)
{
case OperatingSystemPlatform.MacOS:
- {
- string[] suffixPaths = new[]
- {
- "../../runtimes/osx/native/",
- "runtimes/osx/native/",
- string.Empty
- };
- string path = FindLibrary(candidatePaths, suffixPaths, "libmongocrypt.dylib");
- _loader = new DarwinLibraryLoader(path);
- }
+ _loader = new DarwinLibraryLoader(candidatePaths);
break;
case OperatingSystemPlatform.Linux:
- {
- string[] suffixPaths = new[]
- {
- "../../runtimes/linux/native/",
- "runtimes/linux/native/",
- string.Empty
- };
- string path = FindLibrary(candidatePaths, suffixPaths, "libmongocrypt.so");
- _loader = new LinuxLibrary(path);
- }
+ _loader = new LinuxLibrary(candidatePaths);
break;
case OperatingSystemPlatform.Windows:
- {
- string[] suffixPaths = new[]
- {
- @"..\..\runtimes\win\native\",
- @".\runtimes\win\native\",
- string.Empty
- };
- string path = FindLibrary(candidatePaths, suffixPaths, "mongocrypt.dll");
- _loader = new WindowsLibrary(path);
- }
+ _loader = new WindowsLibrary(candidatePaths);
break;
default:
// should not be reached. If we're here, then there is a bug in OperatingSystemHelper
@@ -95,7 +69,7 @@ public LibraryLoader()
}
}
- private string FindLibrary(IList basePaths, string[] suffixPaths, string library)
+ private static string FindLibrary(IList basePaths, string[] suffixPaths, string library)
{
var candidates = new List();
foreach (var basePath in basePaths)
@@ -150,9 +124,17 @@ private class DarwinLibraryLoader : ISharedLibraryLoader
public const int RTLD_GLOBAL = 0x8;
public const int RTLD_NOW = 0x2;
+ private static readonly string[] __suffixPaths =
+ {
+ "../../runtimes/osx/native/",
+ "runtimes/osx/native/",
+ string.Empty
+ };
+
private readonly IntPtr _handle;
- public DarwinLibraryLoader(string path)
+ public DarwinLibraryLoader(List candidatePaths)
{
+ var path = __libmongocryptLibPath ?? FindLibrary(candidatePaths, __suffixPaths, "libmongocrypt.dylib");
_handle = dlopen(path, RTLD_GLOBAL | RTLD_NOW);
if (_handle == IntPtr.Zero)
{
@@ -186,10 +168,18 @@ private class LinuxLibrary : ISharedLibraryLoader
// #define RTLD_GLOBAL 0x100
public const int RTLD_GLOBAL = 0x100;
public const int RTLD_NOW = 0x2;
+
+ private static readonly string[] __suffixPaths =
+ {
+ "../../runtimes/linux/native/",
+ "runtimes/linux/native/",
+ string.Empty
+ };
private readonly IntPtr _handle;
- public LinuxLibrary(string path)
+ public LinuxLibrary(List candidatePaths)
{
+ var path = __libmongocryptLibPath ?? FindLibrary(candidatePaths, __suffixPaths, "libmongocrypt.so");
_handle = dlopen(path, RTLD_GLOBAL | RTLD_NOW);
if (_handle == IntPtr.Zero)
{
@@ -216,9 +206,17 @@ public IntPtr GetFunction(string name)
///
private class WindowsLibrary : ISharedLibraryLoader
{
+ private static readonly string[] __suffixPaths =
+ {
+ @"..\..\runtimes\win\native\",
+ @".\runtimes\win\native\",
+ string.Empty
+ };
+
private readonly IntPtr _handle;
- public WindowsLibrary(string path)
+ public WindowsLibrary(List candidatePaths)
{
+ var path = __libmongocryptLibPath ?? FindLibrary(candidatePaths, __suffixPaths, "mongocrypt.dll");
_handle = LoadLibrary(path);
if (_handle == IntPtr.Zero)
{
diff --git a/bindings/cs/README.md b/bindings/cs/README.md
index 0098e2c63..2469dc1a4 100644
--- a/bindings/cs/README.md
+++ b/bindings/cs/README.md
@@ -54,6 +54,9 @@ If you see `Windows Error: 126` during tests, like the example below, it means t
2. cd /bindings/cs
3. dotnet build cs.build
```
+*Note*: You can use the ```LIBMONGOCRYPT_PATH``` environment variable to load a locally installed
+libmongocrypt build. You should specify the absolute path to the libmongocrypt library itself, not just the containing folder. For example on Linux:
+```$ export LIBMONGOCRYPT_PATH='/path/to/libmongocrypt.so'```.
# Testing
Do not modify xunit.runner.json
diff --git a/bindings/cs/Scripts/build.cake b/bindings/cs/Scripts/build.cake
index cbec5b5ee..19173334f 100644
--- a/bindings/cs/Scripts/build.cake
+++ b/bindings/cs/Scripts/build.cake
@@ -72,7 +72,7 @@ Task("Prepare")
libmongocryptAllDirectory.Combine("ubuntu1804-64").Combine("nocrypto").Combine("lib").CombineWithFilePath("libmongocrypt.so"),
downloadedMongocryptDirectory.CombineWithFilePath("libmongocrypt.so"));
CopyFile(
- libmongocryptAllDirectory.Combine("macos").Combine("nocrypto").Combine("lib").CombineWithFilePath("libmongocrypt.dylib"),
+ libmongocryptAllDirectory.Combine("macos").Combine("lib").CombineWithFilePath("libmongocrypt.dylib"),
downloadedMongocryptDirectory.CombineWithFilePath("libmongocrypt.dylib"));
CopyDirectory(downloadedMongocryptDirectory, libmongocryptRelWithDebInfoDirectory);
});