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); });