/*
* Copyright 2019–present MongoDB, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
namespace MongoDB.Libmongocrypt
{
///
/// A factory for CryptClients.
///
public class CryptClientFactory
{
// MUST be static fields since otherwise these callbacks can be collected via the garbage collector
// regardless they're used by mongocrypt level or no
private static Library.Delegates.CryptoCallback __cryptoAes256EcbEncryptCallback = new Library.Delegates.CryptoCallback(CipherCallbacks.EncryptEcb);
private static Library.Delegates.CryptoCallback __cryptoAes256CbcDecryptCallback = new Library.Delegates.CryptoCallback(CipherCallbacks.DecryptCbc);
private static Library.Delegates.CryptoCallback __cryptoAes256CbcEncryptCallback = new Library.Delegates.CryptoCallback(CipherCallbacks.EncryptCbc);
private static Library.Delegates.HashCallback __cryptoHashCallback = new Library.Delegates.HashCallback(HashCallback.Hash);
private static Library.Delegates.CryptoHmacCallback __cryptoHmacSha256Callback = new Library.Delegates.CryptoHmacCallback(HmacShaCallbacks.HmacSha256);
private static Library.Delegates.CryptoHmacCallback __cryptoHmacSha512Callback = new Library.Delegates.CryptoHmacCallback(HmacShaCallbacks.HmacSha512);
private static Library.Delegates.RandomCallback __randomCallback = new Library.Delegates.RandomCallback(SecureRandomCallback.GenerateRandom);
private static Library.Delegates.CryptoHmacCallback __signRsaesPkcs1HmacCallback = new Library.Delegates.CryptoHmacCallback(SigningRSAESPKCSCallback.RsaSign);
/// Creates a CryptClient with the specified options.
/// The options.
/// A CryptClient
public static CryptClient Create(CryptOptions options)
{
MongoCryptSafeHandle handle = null;
Status status = null;
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)
{
handle.Check(
status,
Library.mongocrypt_setopt_crypto_hooks(
handle,
__cryptoAes256CbcEncryptCallback,
__cryptoAes256CbcDecryptCallback,
__randomCallback,
__cryptoHmacSha512Callback,
__cryptoHmacSha256Callback,
__cryptoHashCallback,
IntPtr.Zero));
handle.Check(
status,
Library.mongocrypt_setopt_crypto_hook_sign_rsaes_pkcs1_v1_5(
handle,
__signRsaesPkcs1HmacCallback,
IntPtr.Zero));
handle.Check(
status,
Library.mongocrypt_setopt_aes_256_ecb(
handle,
__cryptoAes256EcbEncryptCallback,
IntPtr.Zero));
}
foreach (var kmsCredentials in options.KmsCredentials)
{
kmsCredentials.SetCredentials(handle, status);
}
if (options.Schema != null)
{
PinnedBinary.RunAsPinnedBinary(handle, options.Schema, status, (h, pb) => Library.mongocrypt_setopt_schema_map(h, pb));
}
if (options.EncryptedFieldsMap != null)
{
PinnedBinary.RunAsPinnedBinary(handle, options.EncryptedFieldsMap, status, (h, pb) => Library.mongocrypt_setopt_encrypted_field_config_map(h, pb));
}
if (options.BypassQueryAnalysis)
{
Library.mongocrypt_setopt_bypass_query_analysis(handle);
}
if (options.CryptSharedLibPath != null)
{
Library.mongocrypt_setopt_set_crypt_shared_lib_path_override(handle, options.CryptSharedLibPath);
}
if (options.CryptSharedLibSearchPath != null)
{
Library.mongocrypt_setopt_append_crypt_shared_lib_search_path(handle, options.CryptSharedLibSearchPath);
}
Library.mongocrypt_setopt_use_need_kms_credentials_state(handle);
Library.mongocrypt_init(handle);
if (options.IsCryptSharedLibRequired)
{
var versionPtr = Library.mongocrypt_crypt_shared_lib_version_string(handle, out _);
if (versionPtr == IntPtr.Zero)
{
throw new CryptException(Library.StatusType.MONGOCRYPT_STATUS_ERROR_CLIENT, uint.MaxValue, "CryptSharedLib is required, but was not found or not loaded.");
}
}
}
catch
{
handle?.Dispose();
status?.Dispose();
throw;
}
return new CryptClient(handle, status);
}
}
}