Skip to content

Commit

Permalink
CSHARP-4944: Enable use of native crypto in libmongocrypt bindings (#796
Browse files Browse the repository at this point in the history
)
  • Loading branch information
adelinowona authored May 23, 2024
1 parent 7f396b8 commit 1a94335
Show file tree
Hide file tree
Showing 5 changed files with 54 additions and 39 deletions.
12 changes: 8 additions & 4 deletions bindings/cs/MongoDB.Libmongocrypt/CryptClientFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
*/

using System;
using System.Linq;

namespace MongoDB.Libmongocrypt
{
Expand All @@ -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");

/// <summary>Creates a CryptClient with the specified options.</summary>
/// <param name="options">The options.</param>
/// <returns>A CryptClient</returns>
Expand All @@ -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,
Expand Down
10 changes: 10 additions & 0 deletions bindings/cs/MongoDB.Libmongocrypt/Library.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@ static Library()
_mongocrypt_ctx_setopt_key_encryption_key = new Lazy<Delegates.mongocrypt_ctx_setopt_key_encryption_key>(
() => __loader.Value.GetFunction<Delegates.mongocrypt_ctx_setopt_key_encryption_key>(
("mongocrypt_ctx_setopt_key_encryption_key")), true);

_mongocrypt_is_crypto_available = new Lazy<Delegates.mongocrypt_is_crypto_available>(
() => __loader.Value.GetFunction<Delegates.mongocrypt_is_crypto_available>("mongocrypt_is_crypto_available"), true);

_mongocrypt_setopt_aes_256_ecb = new Lazy<Delegates.mongocrypt_setopt_aes_256_ecb>(
() => __loader.Value.GetFunction<Delegates.mongocrypt_setopt_aes_256_ecb>(
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -306,6 +311,8 @@ public static string Version

private static readonly Lazy<Delegates.mongocrypt_setopt_kms_providers> _mongocrypt_setopt_kms_providers;
private static readonly Lazy<Delegates.mongocrypt_ctx_setopt_key_encryption_key> _mongocrypt_ctx_setopt_key_encryption_key;

private static readonly Lazy<Delegates.mongocrypt_is_crypto_available> _mongocrypt_is_crypto_available;

private static readonly Lazy<Delegates.mongocrypt_setopt_aes_256_ecb> _mongocrypt_setopt_aes_256_ecb;
private static readonly Lazy<Delegates.mongocrypt_setopt_bypass_query_analysis> _mongocrypt_setopt_bypass_query_analysis;
Expand Down Expand Up @@ -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);

Expand Down
66 changes: 32 additions & 34 deletions bindings/cs/MongoDB.Libmongocrypt/LibraryLoader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ namespace MongoDB.Libmongocrypt
internal class LibraryLoader
{
private ISharedLibraryLoader _loader;
private static readonly string __libmongocryptLibPath = Environment.GetEnvironmentVariable("LIBMONGOCRYPT_PATH");

public LibraryLoader()
{
Expand All @@ -54,48 +55,21 @@ 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
throw new PlatformNotSupportedException("Unsupported operating system.");
}
}

private string FindLibrary(IList<string> basePaths, string[] suffixPaths, string library)
private static string FindLibrary(IList<string> basePaths, string[] suffixPaths, string library)
{
var candidates = new List<string>();
foreach (var basePath in basePaths)
Expand Down Expand Up @@ -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<string> candidatePaths)
{
var path = __libmongocryptLibPath ?? FindLibrary(candidatePaths, __suffixPaths, "libmongocrypt.dylib");
_handle = dlopen(path, RTLD_GLOBAL | RTLD_NOW);
if (_handle == IntPtr.Zero)
{
Expand Down Expand Up @@ -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<string> candidatePaths)
{
var path = __libmongocryptLibPath ?? FindLibrary(candidatePaths, __suffixPaths, "libmongocrypt.so");
_handle = dlopen(path, RTLD_GLOBAL | RTLD_NOW);
if (_handle == IntPtr.Zero)
{
Expand All @@ -216,9 +206,17 @@ public IntPtr GetFunction(string name)
/// </summary>
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<string> candidatePaths)
{
var path = __libmongocryptLibPath ?? FindLibrary(candidatePaths, __suffixPaths, "mongocrypt.dll");
_handle = LoadLibrary(path);
if (_handle == IntPtr.Zero)
{
Expand Down
3 changes: 3 additions & 0 deletions bindings/cs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ If you see `Windows Error: 126` during tests, like the example below, it means t
2. cd <build>/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
Expand Down
2 changes: 1 addition & 1 deletion bindings/cs/Scripts/build.cake
Original file line number Diff line number Diff line change
Expand Up @@ -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);
});
Expand Down

0 comments on commit 1a94335

Please sign in to comment.