+ * Functions like @ref mongocrypt_ctx_encrypt_init follow a pattern to expose a + * status. A boolean is returned. True indicates success, and false indicates + * failure. On failure a status on the handle is set, and is accessible with a + * corresponding status function. E.g. @ref mongocrypt_ctx_status. + */ + public static class mongocrypt_status_t extends PointerType { + } + + /** + * Contains all options passed on initialization of a @ref mongocrypt_ctx_t. + */ + public static class mongocrypt_opts_t extends PointerType { + } + + /** + * A non-owning view of a byte buffer. + *
+ * Functions returning a mongocrypt_binary_t* expect it to be destroyed with + * mongocrypt_binary_destroy. + */ + public static class mongocrypt_binary_t extends PointerType { + // The `mongocrypt_binary_t` struct layout is part of libmongocrypt's ABI: + // typedef struct _mongocrypt_binary_t { + // void *data; + // uint32_t len; + // } mongocrypt_binary_t; + // To improve performance, fields are read directly using `getPointer` and `getInt`. + // This results in observed performance improvements over using of `mongocrypt_binary_data` and `mongocrypt_binary_len`. Refer: MONGOCRYPT-589. + public mongocrypt_binary_t() { + super(); + } + public Pointer data() { + return this.getPointer().getPointer(0); + } + public int len() { + int len = this.getPointer().getInt(Native.POINTER_SIZE); + // mongocrypt_binary_t represents length as an unsigned `uint32_t`. + // Representing `uint32_t` values greater than INT32_MAX is represented as a negative `int`. + // Throw an exception. mongocrypt_binary_t is not expected to use lengths greater than INT32_MAX. + if (len < 0) { + throw new AssertionError( + String.format("Expected mongocrypt_binary_t length to be non-negative, got: %d", len)); + } + return len; + + } + } + + /** + * The top-level handle to libmongocrypt. + *
+ * Create a mongocrypt_t handle to perform operations within libmongocrypt: + * encryption, decryption, registering log callbacks, etc. + *
+ * Functions on a mongocrypt_t are thread safe, though functions on derived + * handle (e.g. mongocrypt_encryptor_t) are not and must be owned by a single + * thread. See each handle's documentation for thread-safety considerations. + *
+ * Multiple mongocrypt_t handles may be created. + */ + public static class mongocrypt_t extends PointerType { + } + + /** + * Manages the state machine for encryption or decryption. + */ + public static class mongocrypt_ctx_t extends PointerType { + } + + /** + * Manages a single KMS HTTP request/response. + */ + public static class mongocrypt_kms_ctx_t extends PointerType { + } + + /** + * Returns the version string x.y.z for libmongocrypt. + * + * @param len an optional length of the returned string. May be NULL. + * @return the version string x.y.z for libmongocrypt. + */ + public static native cstring + mongocrypt_version(Pointer len); + + + /** + * Create a new non-owning view of a buffer (data + length). + *
+ * Use this to create a mongocrypt_binary_t used for output parameters. + * + * @return A new mongocrypt_binary_t. + */ + public static native mongocrypt_binary_t + mongocrypt_binary_new(); + + + /** + * Create a new non-owning view of a buffer (data + length). + * + * @param data A pointer to an array of bytes. This is not copied. data must outlive the binary object. + * @param len The length of the @p data byte array. + * @return A new mongocrypt_binary_t. + */ + public static native mongocrypt_binary_t + mongocrypt_binary_new_from_data(Pointer data, int len); + + + /** + * Get a pointer to the referenced data. + * + * @param binary The @ref mongocrypt_binary_t. + * @return A pointer to the referenced data. + */ + public static native Pointer + mongocrypt_binary_data(mongocrypt_binary_t binary); + + + /** + * Get the length of the referenced data. + * + * @param binary The @ref mongocrypt_binary_t. + * @return The length of the referenced data. + */ + public static native int + mongocrypt_binary_len(mongocrypt_binary_t binary); + + + /** + * Free the @ref mongocrypt_binary_t. + *
+ * This does not free the referenced data. Refer to individual function + * documentation to determine the lifetime guarantees of the underlying + * data. + * + * @param binary The mongocrypt_binary_t destroy. + */ + public static native void + mongocrypt_binary_destroy(mongocrypt_binary_t binary); + + + public static final int MONGOCRYPT_STATUS_OK = 0; + public static final int MONGOCRYPT_STATUS_ERROR_CLIENT = 1; + public static final int MONGOCRYPT_STATUS_ERROR_KMS = 2; + + /** + * Create a new status object. + *
+ * Use a new status object to retrieve the status from a handle by passing + * this as an out-parameter to functions like @ref mongocrypt_ctx_status. + * When done, destroy it with @ref mongocrypt_status_destroy. + * + * @return A new status object. + */ + public static native mongocrypt_status_t + mongocrypt_status_new(); + + /** + * Set a status object with message, type, and code. + *
+ * Use this to set the mongocrypt_status_t given in the crypto hooks. + * + * @param status The status. + * @param type The status type. + * @param code The status code. + * @param message The message. + * @param message_len The length of @p message. Pass -1 to determine the * string length with strlen (must * be NULL terminated). + */ + public static native void + mongocrypt_status_set(mongocrypt_status_t status, + int type, + int code, + cstring message, + int message_len); + + /** + * Indicates success or the type of error. + * + * @param status The status object. + * @return A @ref mongocrypt_status_type_t. + */ + + public static native int + mongocrypt_status_type(mongocrypt_status_t status); + + + /** + * Get an error code or 0. + * + * @param status The status object. + * @return An error code. + */ + public static native int + mongocrypt_status_code(mongocrypt_status_t status); + + + /** + * Get the error message associated with a status, or an empty string. + * + * @param status The status object. + * @param len an optional length of the returned string. May be NULL. + * @return An error message or an empty string. + */ + public static native cstring + mongocrypt_status_message(mongocrypt_status_t status, Pointer len); + + + /** + * Returns true if the status indicates success. + * + * @param status The status to check. + * @return A boolean indicating success. + */ + public static native boolean + mongocrypt_status_ok(mongocrypt_status_t status); + + + /** + * Free the memory for a status object. + * + * @param status The status to destroy. + */ + public static native void + mongocrypt_status_destroy(mongocrypt_status_t status); + + + public static final int MONGOCRYPT_LOG_LEVEL_FATAL = 0; + public static final int MONGOCRYPT_LOG_LEVEL_ERROR = 1; + public static final int MONGOCRYPT_LOG_LEVEL_WARNING = 2; + public static final int MONGOCRYPT_LOG_LEVEL_INFO = 3; + public static final int MONGOCRYPT_LOG_LEVEL_TRACE = 4; + + + /** + * A log callback function. Set a custom log callback with mongocrypt_setopt_log_handler. + */ + public interface mongocrypt_log_fn_t extends Callback { + void log(int level, cstring message, int message_len, Pointer ctx); + } + + public interface mongocrypt_crypto_fn extends Callback { + boolean crypt(Pointer ctx, mongocrypt_binary_t key, mongocrypt_binary_t iv, mongocrypt_binary_t in, + mongocrypt_binary_t out, Pointer bytesWritten, mongocrypt_status_t status); + } + + public interface mongocrypt_hmac_fn extends Callback { + boolean hmac(Pointer ctx, mongocrypt_binary_t key, mongocrypt_binary_t in, mongocrypt_binary_t out, + mongocrypt_status_t status); + } + + public interface mongocrypt_hash_fn extends Callback { + boolean hash(Pointer ctx, mongocrypt_binary_t in, mongocrypt_binary_t out, mongocrypt_status_t status); + } + + public interface mongocrypt_random_fn extends Callback { + boolean random(Pointer ctx, mongocrypt_binary_t out, int count, mongocrypt_status_t status); + } + + /** + * Allocate a new @ref mongocrypt_t object. + *
+ * Initialize with @ref mongocrypt_init. When done, free with @ref + * mongocrypt_destroy. + * + * @return A new @ref mongocrypt_t object. + */ + public static native mongocrypt_t + mongocrypt_new(); + + /** + * Set a handler to get called on every log message. + * + * @param crypt The @ref mongocrypt_t object. + * @param log_fn The log callback. + * @param log_ctx A context passed as an argument to the log callback every + * invokation. + * @return A boolean indicating success. + */ + public static native boolean + mongocrypt_setopt_log_handler(mongocrypt_t crypt, + mongocrypt_log_fn_t log_fn, + Pointer log_ctx); + + + public static native boolean + mongocrypt_setopt_crypto_hooks(mongocrypt_t crypt, + mongocrypt_crypto_fn aes_256_cbc_encrypt, + mongocrypt_crypto_fn aes_256_cbc_decrypt, + mongocrypt_random_fn random, + mongocrypt_hmac_fn hmac_sha_512, + mongocrypt_hmac_fn hmac_sha_256, + mongocrypt_hash_fn sha_256, + Pointer ctx); + + /** + * Set a crypto hook for the AES256-CTR operations. + * + * @param crypt The @ref mongocrypt_t object. + * @param aes_256_ctr_encrypt The crypto callback function for encrypt + * operation. + * @param aes_256_ctr_decrypt The crypto callback function for decrypt + * operation. + * @param ctx A context passed as an argument to the crypto callback + * every invocation. + * @return A boolean indicating success. If false, an error status is set. + * Retrieve it with @ref mongocrypt_status + * + */ + public static native boolean + mongocrypt_setopt_aes_256_ctr (mongocrypt_t crypt, + mongocrypt_crypto_fn aes_256_ctr_encrypt, + mongocrypt_crypto_fn aes_256_ctr_decrypt, + Pointer ctx); + + /** + * Set a crypto hook for the RSASSA-PKCS1-v1_5 algorithm with a SHA-256 hash. + * + *
See: https://tools.ietf.org/html/rfc3447#section-8.2
+ * + *Note: this function has the wrong name. It should be: + * mongocrypt_setopt_crypto_hook_sign_rsassa_pkcs1_v1_5
+ * + * @param crypt The @ref mongocrypt_t object. + * @param sign_rsaes_pkcs1_v1_5 The crypto callback function. + * @param sign_ctx A context passed as an argument to the crypto callback + * every invocation. + * @return A boolean indicating success. If false, an error status is set. + * Retrieve it with @ref mongocrypt_status + */ + public static native boolean + mongocrypt_setopt_crypto_hook_sign_rsaes_pkcs1_v1_5( + mongocrypt_t crypt, + mongocrypt_hmac_fn sign_rsaes_pkcs1_v1_5, + Pointer sign_ctx); + + /** + * Set a handler to get called on every log message. + * + * @param crypt The @ref mongocrypt_t object. + * @param aws_access_key_id The AWS access key ID used to generate KMS + * messages. + * @param aws_access_key_id_len The string length (in bytes) of @p + * * aws_access_key_id. Pass -1 to determine the string length with strlen (must + * * be NULL terminated). + * @param aws_secret_access_key The AWS secret access key used to generate + * KMS messages. + * @param aws_secret_access_key_len The string length (in bytes) of @p + * aws_secret_access_key. Pass -1 to determine the string length with strlen + * (must be NULL terminated). + * @return A boolean indicating success. + */ + public static native boolean + mongocrypt_setopt_kms_provider_aws(mongocrypt_t crypt, + cstring aws_access_key_id, + int aws_access_key_id_len, + cstring aws_secret_access_key, + int aws_secret_access_key_len); + + /** + * Configure a local KMS provider on the @ref mongocrypt_t object. + * + * @param crypt The @ref mongocrypt_t object. + * @param key A 64 byte master key used to encrypt and decrypt key vault keys. + * @return A boolean indicating success. + */ + public static native boolean + mongocrypt_setopt_kms_provider_local(mongocrypt_t crypt, + mongocrypt_binary_t key); + + /** + * Configure KMS providers with a BSON document. + * + * @param crypt The @ref mongocrypt_t object. + * @param kms_providers A BSON document mapping the KMS provider names to credentials. + * @return A boolean indicating success. If false, an error status is set. + * @since 1.1 + */ + public static native boolean + mongocrypt_setopt_kms_providers(mongocrypt_t crypt, + mongocrypt_binary_t kms_providers); + + /** + * Set a local schema map for encryption. + * + * @param crypt The @ref mongocrypt_t object. + * @param schema_map A BSON document representing the schema map supplied by + * the user. The keys are collection namespaces and values are JSON schemas. + * @return A boolean indicating success. If false, an error status is set. + * Retrieve it with @ref mongocrypt_status + */ + public static native boolean + mongocrypt_setopt_schema_map (mongocrypt_t crypt, mongocrypt_binary_t schema_map); + + /** + * Opt-into setting KMS providers before each KMS request. + * + * If set, before entering the MONGOCRYPT_CTX_NEED_KMS state, + * contexts will enter the MONGOCRYPT_CTX_NEED_KMS_CREDENTIALS state + * and then wait for credentials to be supplied through @ref mongocrypt_ctx_provide_kms_providers. + * + * @param crypt The @ref mongocrypt_t object to update + */ + public static native void + mongocrypt_setopt_use_need_kms_credentials_state (mongocrypt_t crypt); + + + /** + * Set a local EncryptedFieldConfigMap for encryption. + * + * @param crypt The @ref mongocrypt_t object. + * @param encryptedFieldConfigMap A BSON document representing the EncryptedFieldConfigMap + * supplied by the user. The keys are collection namespaces and values are + * EncryptedFieldConfigMap documents. The viewed data copied. It is valid to + * destroy @p efc_map with @ref mongocrypt_binary_destroy immediately after. + * @return A boolean indicating success. If false, an error status is set. + * Retrieve it with @ref mongocrypt_status + */ + public static native boolean + mongocrypt_setopt_encrypted_field_config_map (mongocrypt_t crypt, mongocrypt_binary_t encryptedFieldConfigMap); + + /** + * Opt-into skipping query analysis. + * + *If opted in: + *
If no crypt_shared dynamic library is found in any of the directories + * specified by the search paths loaded here, @ref mongocrypt_init() will still + * succeed and continue to operate without crypt_shared.
+ * + *The search paths are searched in the order that they are appended. This + * allows one to provide a precedence in how the library will be discovered. For + * example, appending known directories before appending "$SYSTEM" will allow + * one to supersede the system's installed library, but still fall-back to it if + * the library wasn't found otherwise. If one does not ever append "$SYSTEM", + * then the system's library-search mechanism will never be consulted.
+ * + *If an absolute path to the library is specified using @ref mongocrypt_setopt_set_crypt_shared_lib_path_override, + * then paths appended here will have no effect.
+ * @since 1.5 + */ + public static native void + mongocrypt_setopt_append_crypt_shared_lib_search_path (mongocrypt_t crypt, cstring path); + + /** + * Set a single override path for loading the crypt_shared dynamic library. + * @param crypt The @ref mongocrypt_t object to update + * @param path A null-terminated sequence of bytes for a path to the crypt_shared + * dynamic library. On some filesystems, this may be arbitrary bytes. On other + * filesystems, this may be required to be a valid UTF-8 code unit sequence. If + * the leading element of the path is the literal string `$ORIGIN`, that + * substring will be replaced with the directory path containing the executable + * libmongocrypt module. + * + *This function will do no IO nor path validation. All validation will + * occur during the call to @ref mongocrypt_init.
+ *If a crypt_shared library path override is specified here, then no paths given + * to @ref mongocrypt_setopt_append_crypt_shared_lib_search_path will be consulted when + * opening the crypt_shared library.
+ *If a path is provided via this API and @ref mongocrypt_init fails to + * initialize a valid crypt_shared library instance for the path specified, then + * the initialization of mongocrypt_t will fail with an error.
+ * @since 1.5 + */ + public static native void + mongocrypt_setopt_set_crypt_shared_lib_path_override(mongocrypt_t crypt, cstring path); + + /** + * Set the query type to use for Queryable Encryption explicit encryption. + * The query type is only used for indexed Queryable Encryption. + * + * @param ctx The @ref mongocrypt_ctx_t object. + * @param query_type the query type + * @param len the length + * @return A boolean indicating success. If false, an error status is set. + * Retrieve it with @ref mongocrypt_ctx_status + */ + public static native boolean + mongocrypt_ctx_setopt_query_type (mongocrypt_ctx_t ctx, cstring query_type, int len); + + /** + * Set options for explicit encryption with the "range" algorithm. + * NOTE: "range" is currently unstable API and subject to backwards breaking changes. + * + * opts is a BSON document of the form: + * { + * "min": Optional<BSON value>, + * "max": Optional<BSON value>, + * "sparsity": Int64, + * "precision": Optional<Int32> + * "trimFactor": Optional<Int32> + * } + * + * @param ctx The @ref mongocrypt_ctx_t object. + * @param opts BSON. + * @return A boolean indicating success. If false, an error status is set. + * @since 1.7 + */ + public static native boolean + mongocrypt_ctx_setopt_algorithm_range (mongocrypt_ctx_t ctx, mongocrypt_binary_t opts); + + /** + * Initialize new @ref mongocrypt_t object. + * + * @param crypt The @ref mongocrypt_t object. + * @return A boolean indicating success. Failure may occur if previously set options are invalid. + */ + public static native boolean + mongocrypt_init(mongocrypt_t crypt); + + + /** + * Get the status associated with a @ref mongocrypt_t object. + * + * @param crypt The @ref mongocrypt_t object. + * @param status Receives the status. + * @return A boolean indicating success. + */ + public static native boolean + mongocrypt_status(mongocrypt_t crypt, mongocrypt_status_t status); + + /** + * Returns true if libmongocrypt was built with native crypto support. + * + *+ * If libmongocrypt was not built with native crypto support, setting crypto hooks is required. + *
+ * + * @return true if libmongocrypt was built with native crypto support + */ + public static native boolean + mongocrypt_is_crypto_available(); + + /** + * Destroy the @ref mongocrypt_t object. + * + * @param crypt The @ref mongocrypt_t object to destroy. + */ + public static native void + mongocrypt_destroy(mongocrypt_t crypt); + + /** + * Obtain a nul-terminated version string of the loaded crypt_shared dynamic library, + * if available. + * + * If no crypt_shared was successfully loaded, this function returns NULL. + * + * @param crypt The mongocrypt_t object after a successful call to mongocrypt_init. + * @param len an optional length of the returned string. May be NULL. + * + * @return A nul-terminated string of the dynamically loaded crypt_shared library. + * @since 1.5 + */ + public static native cstring + mongocrypt_crypt_shared_lib_version_string (mongocrypt_t crypt, Pointer len); + + /** + * Call in response to the MONGOCRYPT_CTX_NEED_KMS_CREDENTIALS state + * to set per-context KMS provider settings. These follow the same format + * as @ref mongocrypt_setopt_kms_providers. If no keys are present in the + * BSON input, the KMS provider settings configured for the @ref mongocrypt_t + * at initialization are used. + * + * @param ctx The @ref mongocrypt_ctx_t object. + * @param kms_providers A BSON document mapping the KMS provider names + * to credentials. + * @return A boolean indicating success. If false, an error status is set. + * Retrieve it with @ref mongocrypt_ctx_status. + */ + public static native boolean + mongocrypt_ctx_provide_kms_providers (mongocrypt_ctx_t ctx, + mongocrypt_binary_t kms_providers); + + /** + * Set the key id to use for explicit encryption. + * + * @param ctx The @ref mongocrypt_ctx_t object. + * @param key_id The key_id to use. + * @return A boolean indicating success. + */ + public static native boolean + mongocrypt_ctx_setopt_key_id (mongocrypt_ctx_t ctx, + mongocrypt_binary_t key_id); + + /** + * Set the keyAltName to use for explicit encryption. + * keyAltName should be a binary encoding a bson document + * with the following format:{ "keyAltName" : >BSON UTF8 value< }
+ *
+ * It is an error to set both this and the key id.
+ * + * @param ctx The @ref mongocrypt_ctx_t object. + * @param key_alt_name The name to use. + * @return A boolean indicating success. If false, an error status is set. + * Retrieve it with @ref mongocrypt_ctx_status + */ + public static native boolean + mongocrypt_ctx_setopt_key_alt_name (mongocrypt_ctx_t ctx, + mongocrypt_binary_t key_alt_name); + + /** + * Set the keyMaterial to use for encrypting data. + * + *
+ * Pass the binary encoding of a BSON document like the following:
+ * { "keyMaterial" : (BSON BINARY value) }
+ *
+ * Initialize the context with functions like @ref mongocrypt_ctx_encrypt_init. + * When done, destroy it with @ref mongocrypt_ctx_destroy. + * + * @param crypt The @ref mongocrypt_t object. + * @return A new context. + */ + public static native mongocrypt_ctx_t + mongocrypt_ctx_new(mongocrypt_t crypt); + + + /** + * Get the status associated with a @ref mongocrypt_ctx_t object. + * + * @param ctx The @ref mongocrypt_ctx_t object. + * @param status Receives the status. + * @return A boolean indicating success. + */ + + public static native boolean + mongocrypt_ctx_status(mongocrypt_ctx_t ctx, mongocrypt_status_t status); + + + /** + * Identify the AWS KMS master key to use for creating a data key. + * + * @param ctx The @ref mongocrypt_ctx_t object. + * @param region The AWS region. + * @param region_len The string length of @p region. Pass -1 to determine + * the string length with strlen (must be NULL terminated). + * @param cmk The Amazon Resource Name (ARN) of the customer master key + * (CMK). + * @param cmk_len The string length of @p cmk_len. Pass -1 to determine the + * string length with strlen (must be NULL terminated). + * @return A boolean indicating success. + */ + public static native boolean + mongocrypt_ctx_setopt_masterkey_aws (mongocrypt_ctx_t ctx, + cstring region, + int region_len, + cstring cmk, + int cmk_len); + + /** + * Identify a custom AWS endpoint when creating a data key. + * This is used internally to construct the correct HTTP request + * (with the Host header set to this endpoint). This endpoint + * is persisted in the new data key, and will be returned via + * mongocrypt_kms_ctx_endpoint. + * + * @param ctx The @ref mongocrypt_ctx_t object. + * @param endpoint The endpoint. + * @param endpoint_len The string length of @p endpoint. Pass -1 to + * determine the string length with strlen (must be NULL terminated). + * @return A boolean indicating success. If false, an error status is set. + * Retrieve it with @ref mongocrypt_ctx_status + */ + public static native boolean + mongocrypt_ctx_setopt_masterkey_aws_endpoint (mongocrypt_ctx_t ctx, + cstring endpoint, + int endpoint_len); + + + /** + * Set the master key to "local" for creating a data key. + * + * @param ctx The @ref mongocrypt_ctx_t object. + * @return A boolean indicating success. + */ + public static native boolean + mongocrypt_ctx_setopt_masterkey_local (mongocrypt_ctx_t ctx); + + /** + * Set key encryption key document for creating a data key. + * + * @param ctx The @ref mongocrypt_ctx_t object. + * @param keyDocument BSON representing the key encryption key document. + * @return A boolean indicating success. If false, and error status is set. + * @since 1.1 + */ + public static native boolean + mongocrypt_ctx_setopt_key_encryption_key(mongocrypt_ctx_t ctx, + mongocrypt_binary_t keyDocument); + + /** + * Initialize a context to create a data key. + * + * Set options before using @ref mongocrypt_ctx_setopt_masterkey_aws and + * mongocrypt_ctx_setopt_masterkey_local. + * + * @param ctx The @ref mongocrypt_ctx_t object. + * @return A boolean indicating success. + * + * Assumes a master key option has been set, and an associated KMS provider + * has been set on the parent @ref mongocrypt_t. + */ + public static native boolean + mongocrypt_ctx_datakey_init (mongocrypt_ctx_t ctx); + + /** + * Initialize a context for encryption. + * + * Associated options: + * - @ref mongocrypt_ctx_setopt_cache_noblock + * - @ref mongocrypt_ctx_setopt_schema + * + * @param ctx The @ref mongocrypt_ctx_t object. + * @param db The database name. + * @param db_len The byte length of @p db. Pass -1 to determine the string length with strlen (must be NULL terminated). + * @param cmd The BSON command to be encrypted. + * @return A boolean indicating success. If false, an error status is set. + * Retrieve it with @ref mongocrypt_ctx_status + */ + public static native boolean + mongocrypt_ctx_encrypt_init(mongocrypt_ctx_t ctx, + cstring db, + int db_len, + mongocrypt_binary_t cmd); + + /** + * Explicit helper method to encrypt a single BSON object. Contexts + * created for explicit encryption will not go through mongocryptd. + * + * To specify a key_id, algorithm, or iv to use, please use the + * corresponding mongocrypt_setopt methods before calling this. + * + * This method expects the passed-in BSON to be of the form: + * { "v" : BSON value to encrypt } + * + * @param ctx A @ref mongocrypt_ctx_t. + * @param msg A @ref mongocrypt_binary_t the plaintext BSON value. + * @return A boolean indicating success. + */ + public static native boolean + mongocrypt_ctx_explicit_encrypt_init (mongocrypt_ctx_t ctx, + mongocrypt_binary_t msg); + + /** + * Explicit helper method to encrypt a Match Expression or Aggregate Expression. + * Contexts created for explicit encryption will not go through mongocryptd. + * Requires query_type to be "range". + * NOTE: "range" is currently unstable API and subject to backwards breaking changes. + * + * This method expects the passed-in BSON to be of the form: + * { "v" : FLE2RangeFindDriverSpec } + * + * FLE2RangeFindDriverSpec is a BSON document with one of these forms: + * + * 1. A Match Expression of this form: + * {$and: [{<field>: {<op>: <value1>, {<field>: {<op>: <value2> }}]} + * 2. An Aggregate Expression of this form: + * {$and: [{<op>: [<fieldpath>, <value1>]}, {<op>: [<fieldpath>, <value2>]}] + * + * may be $lt, $lte, $gt, or $gte. + * + * The value of "v" is expected to be the BSON value passed to a driver + * ClientEncryption.encryptExpression helper. + * + * Associated options for FLE 1: + * - @ref mongocrypt_ctx_setopt_key_id + * - @ref mongocrypt_ctx_setopt_key_alt_name + * - @ref mongocrypt_ctx_setopt_algorithm + * + * Associated options for Queryable Encryption: + * - @ref mongocrypt_ctx_setopt_key_id + * - @ref mongocrypt_ctx_setopt_index_key_id + * - @ref mongocrypt_ctx_setopt_contention_factor + * - @ref mongocrypt_ctx_setopt_query_type + * - @ref mongocrypt_ctx_setopt_algorithm_range + * + * An error is returned if FLE 1 and Queryable Encryption incompatible options + * are set. + * + * @param ctx A @ref mongocrypt_ctx_t. + * @param msg A @ref mongocrypt_binary_t the plaintext BSON value. + * @return A boolean indicating success. + * @since 1.7 + */ + public static native boolean + mongocrypt_ctx_explicit_encrypt_expression_init (mongocrypt_ctx_t ctx, + mongocrypt_binary_t msg); + + /** + * Initialize a context for decryption. + * + * @param ctx The mongocrypt_ctx_t object. + * @param doc The document to be decrypted. + * @return A boolean indicating success. + */ + public static native boolean + mongocrypt_ctx_decrypt_init(mongocrypt_ctx_t ctx, mongocrypt_binary_t doc); + + + /** + * Explicit helper method to decrypt a single BSON object. + * + * @param ctx A @ref mongocrypt_ctx_t. + * @param msg A @ref mongocrypt_binary_t the encrypted BSON. + * @return A boolean indicating success. + */ + public static native boolean + mongocrypt_ctx_explicit_decrypt_init (mongocrypt_ctx_t ctx, + mongocrypt_binary_t msg); + + /** + * Initialize a context to rewrap datakeys. + * + * Associated options {@link #mongocrypt_ctx_setopt_key_encryption_key(mongocrypt_ctx_t, mongocrypt_binary_t)} + * + * @param ctx The @ref mongocrypt_ctx_t object. + * @param filter The filter to use for the find command on the key vault collection to retrieve datakeys to rewrap. + * @return A boolean indicating success. If false, and error status is set. + * @since 1.5 + */ + public static native boolean + mongocrypt_ctx_rewrap_many_datakey_init (mongocrypt_ctx_t ctx, mongocrypt_binary_t filter); + + + public static final int MONGOCRYPT_CTX_ERROR = 0; + public static final int MONGOCRYPT_CTX_NEED_MONGO_COLLINFO = 1; /* run on main MongoClient */ + public static final int MONGOCRYPT_CTX_NEED_MONGO_MARKINGS = 2; /* run on mongocryptd. */ + public static final int MONGOCRYPT_CTX_NEED_MONGO_KEYS = 3; /* run on key vault */ + public static final int MONGOCRYPT_CTX_NEED_KMS = 4; + public static final int MONGOCRYPT_CTX_READY = 5; /* ready for encryption/decryption */ + public static final int MONGOCRYPT_CTX_DONE = 6; + public static final int MONGOCRYPT_CTX_NEED_KMS_CREDENTIALS = 7; /* fetch/renew KMS credentials */ + + public static final int MONGOCRYPT_INDEX_TYPE_NONE = 1; + public static final int MONGOCRYPT_INDEX_TYPE_EQUALITY = 2; + public static final int MONGOCRYPT_QUERY_TYPE_EQUALITY = 1; + + /** + * Get the current state of a context. + * + * @param ctx The @ref mongocrypt_ctx_t object. + * @return A @ref mongocrypt_ctx_state_t. + */ + public static native int + mongocrypt_ctx_state(mongocrypt_ctx_t ctx); + + + /** + * Get BSON necessary to run the mongo operation when mongocrypt_ctx_t + * is in MONGOCRYPT_CTX_NEED_MONGO_* states. + * + *
+ * op_bson is a BSON document to be used for the operation. + * - For MONGOCRYPT_CTX_NEED_MONGO_COLLINFO it is a listCollections filter. + * - For MONGOCRYPT_CTX_NEED_MONGO_KEYS it is a find filter. + * - For MONGOCRYPT_CTX_NEED_MONGO_MARKINGS it is a JSON schema to append. + *
+ * + * @param ctx The @ref mongocrypt_ctx_t object. + * @param op_bson A BSON document for the MongoDB operation. + * @return A boolean indicating success. + */ + public static native boolean + mongocrypt_ctx_mongo_op(mongocrypt_ctx_t ctx, mongocrypt_binary_t op_bson); + + + /** + * Feed a BSON reply or result when when mongocrypt_ctx_t is in + * MONGOCRYPT_CTX_NEED_MONGO_* states. This may be called multiple times + * depending on the operation. + *+ * op_bson is a BSON document to be used for the operation. + * - For MONGOCRYPT_CTX_NEED_MONGO_COLLINFO it is a doc from a listCollections + * cursor. + * - For MONGOCRYPT_CTX_NEED_MONGO_KEYS it is a doc from a find cursor. + * - For MONGOCRYPT_CTX_NEED_MONGO_MARKINGS it is a reply from mongocryptd. + * + * @param ctx The @ref mongocrypt_ctx_t object. + * @param reply A BSON document for the MongoDB operation. + * @return A boolean indicating success. + */ + public static native boolean + mongocrypt_ctx_mongo_feed(mongocrypt_ctx_t ctx, mongocrypt_binary_t reply); + + + /** + * Call when done feeding the reply (or replies) back to the context. + * + * @param ctx The @ref mongocrypt_ctx_t object. + * @return A boolean indicating success. + */ + + public static native boolean + mongocrypt_ctx_mongo_done(mongocrypt_ctx_t ctx); + + /** + * Get the next KMS handle. + *
+ * Multiple KMS handles may be retrieved at once. Drivers may do this to fan + * out multiple concurrent KMS HTTP requests. Feeding multiple KMS requests + * is thread-safe. + *
+ * Is KMS handles are being handled synchronously, the driver can reuse the same + * TLS socket to send HTTP requests and receive responses. + * + * @param ctx A @ref mongocrypt_ctx_t. + * @return a new @ref mongocrypt_kms_ctx_t or NULL. + */ + public static native mongocrypt_kms_ctx_t + mongocrypt_ctx_next_kms_ctx(mongocrypt_ctx_t ctx); + + /** + * Get the KMS provider identifier associated with this KMS request. + * + * This is used to conditionally configure TLS connections based on the KMS + * request. It is useful for KMIP, which authenticates with a client + * certificate. + * + * @param kms The mongocrypt_kms_ctx_t object. + * @param len Receives the length of the returned string. + * + * @return The name of the KMS provider + */ + public static native cstring + mongocrypt_kms_ctx_get_kms_provider(mongocrypt_kms_ctx_t kms, + Pointer len); + + /** + * Get the HTTP request message for a KMS handle. + * + * @param kms A @ref mongocrypt_kms_ctx_t. + * @param msg The HTTP request to send to KMS. + * @return A boolean indicating success. + */ + public static native boolean + mongocrypt_kms_ctx_message(mongocrypt_kms_ctx_t kms, + mongocrypt_binary_t msg); + + /** + * Get the hostname from which to connect over TLS. + *
+ * The storage for @p endpoint is not owned by the caller, but + * is valid until calling @ref mongocrypt_ctx_kms_done on the + * parent @ref mongocrypt_ctx_t. + * + * @param kms A @ref mongocrypt_kms_ctx_t. + * @param endpoint The output hostname. + * @return A boolean indicating success. + */ + public static native boolean + mongocrypt_kms_ctx_endpoint(mongocrypt_kms_ctx_t kms, PointerByReference endpoint); + + /** + * Indicates how many bytes to feed into @ref mongocrypt_kms_ctx_feed. + * + * @param kms The @ref mongocrypt_kms_ctx_t. + * @return The number of requested bytes. + */ + public static native int + mongocrypt_kms_ctx_bytes_needed(mongocrypt_kms_ctx_t kms); + + + /** + * Feed bytes from the HTTP response. + *
+ * Feeding more bytes than what has been returned in @ref
+ * mongocrypt_kms_ctx_bytes_needed is an error.
+ *
+ * @param kms The @ref mongocrypt_kms_ctx_t.
+ * @param bytes The bytes to feed.
+ * @return A boolean indicating success.
+ */
+ public static native boolean
+ mongocrypt_kms_ctx_feed(mongocrypt_kms_ctx_t kms, mongocrypt_binary_t bytes);
+
+
+ /**
+ * Get the status associated with a @ref mongocrypt_kms_ctx_t object.
+ *
+ * @param kms The @ref mongocrypt_kms_ctx_t object.
+ * @param status Receives the status.
+ * @return A boolean indicating success.
+ */
+ public static native boolean
+ mongocrypt_kms_ctx_status(mongocrypt_kms_ctx_t kms,
+ mongocrypt_status_t status);
+
+
+ /**
+ * Call when done handling all KMS contexts.
+ *
+ * @param ctx The @ref mongocrypt_ctx_t object.
+ * @return A boolean indicating success.
+ */
+ public static native boolean
+ mongocrypt_ctx_kms_done(mongocrypt_ctx_t ctx);
+
+
+ /**
+ * Perform the final encryption or decryption.
+ *
+ * @param ctx A @ref mongocrypt_ctx_t.
+ * @param out The final BSON to send to the server.
+ * @return a boolean indicating success.
+ */
+ public static native boolean
+ mongocrypt_ctx_finalize(mongocrypt_ctx_t ctx, mongocrypt_binary_t out);
+
+
+ /**
+ * Destroy and free all memory associated with a @ref mongocrypt_ctx_t.
+ *
+ * @param ctx A @ref mongocrypt_ctx_t.
+ */
+ public static native void
+ mongocrypt_ctx_destroy(mongocrypt_ctx_t ctx);
+
+ static final String NATIVE_LIBRARY_NAME = "mongocrypt";
+
+ static {
+ Native.register(CAPI.class, NATIVE_LIBRARY_NAME);
+ }
+}
diff --git a/mongodb-crypt/src/main/com/mongodb/crypt/capi/CAPIHelper.java b/mongodb-crypt/src/main/com/mongodb/crypt/capi/CAPIHelper.java
new file mode 100644
index 00000000000..c1de63e8c8c
--- /dev/null
+++ b/mongodb-crypt/src/main/com/mongodb/crypt/capi/CAPIHelper.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2008-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.
+ *
+ */
+
+package com.mongodb.crypt.capi;
+
+import com.mongodb.crypt.capi.CAPI.mongocrypt_binary_t;
+import com.sun.jna.Pointer;
+import org.bson.BsonBinaryWriter;
+import org.bson.BsonDocument;
+import org.bson.RawBsonDocument;
+import org.bson.codecs.BsonValueCodecProvider;
+import org.bson.codecs.Codec;
+import org.bson.codecs.EncoderContext;
+import org.bson.codecs.configuration.CodecRegistries;
+import org.bson.codecs.configuration.CodecRegistry;
+import org.bson.io.BasicOutputBuffer;
+
+import java.nio.ByteBuffer;
+
+import static com.mongodb.crypt.capi.CAPI.mongocrypt_binary_new_from_data;
+import static java.lang.String.format;
+
+final class CAPIHelper {
+
+ private static final CodecRegistry CODEC_REGISTRY = CodecRegistries.fromProviders(new BsonValueCodecProvider());
+
+ @SuppressWarnings("unchecked")
+ static BinaryHolder toBinary(final BsonDocument document) {
+ BasicOutputBuffer buffer = new BasicOutputBuffer();
+ BsonBinaryWriter writer = new BsonBinaryWriter(buffer);
+ ((Codec Set bypassQueryAnalysis to true to use explicit encryption on indexed fields
+ * without the MongoDB Enterprise Advanced licensed crypt shared library.
+ * Make sure that JNA is able to find the shared library, most likely by setting the jna.library.path system property
+ * To insert or query with an "Indexed" encrypted payload, use a MongoClient configured with {@code AutoEncryptionSettings}.
+ * {@code AutoEncryptionSettings.bypassQueryAnalysis} may be true.
+ * {@code AutoEncryptionSettings.bypassAutoEncryption must be false}. It is an error to set contentionFactor when algorithm is not "Indexed".
+ * @param contentionFactor the contention factor
+ * @return this
+ * @since 1.5
+ */
+ public Builder contentionFactor(final Long contentionFactor) {
+ this.contentionFactor = contentionFactor;
+ return this;
+ }
+
+ /**
+ * The QueryType.
+ *
+ * It is an error to set queryType when algorithm is not "Indexed". It is an error to set rangeOptions when the algorithm is not "range".
+ * Clients should call this method first, and send the message on a TLS connection to a configured KMS server.
+ *
+ * After sending the message to the KMS server, clients should call this method in a loop, receiving {@code bytesNeeded} from
+ * the KMS server and feeding those bytes to this decryptor, until {@code bytesNeeded} is 0.
+ *
+ * After sending the message to the KMS server, clients should call this method in a loop, receiving the number of bytes indicated by
+ * a call to {@link #bytesNeeded()} from the KMS server and feeding those bytes to this decryptor, until {@link #bytesNeeded()}
+ * returns 0.
+ *
+ * The masterKey document MUST have the fields corresponding to the given provider as specified in masterKey.
+ * Logger
instance.
+ *
+ * @return name of this logger instance
+ */
+ String getName();
+
+ /**
+ * Is the logger instance enabled for the TRACE level?
+ *
+ * @return True if this Logger is enabled for the TRACE level, false otherwise.
+ */
+ boolean isTraceEnabled();
+
+ /**
+ * Log a message at the TRACE level.
+ *
+ * @param msg the message string to be logged
+ */
+ void trace(String msg);
+
+ /**
+ * Log an exception (throwable) at the TRACE level with an accompanying message.
+ *
+ * @param msg the message accompanying the exception
+ * @param t the exception (throwable) to log
+ */
+ void trace(String msg, Throwable t);
+
+ /**
+ * Is the logger instance enabled for the DEBUG level?
+ *
+ * @return True if this Logger is enabled for the DEBUG level, false otherwise.
+ */
+ boolean isDebugEnabled();
+
+
+ /**
+ * Log a message at the DEBUG level.
+ *
+ * @param msg the message string to be logged
+ */
+ void debug(String msg);
+
+
+ /**
+ * Log an exception (throwable) at the DEBUG level with an accompanying message.
+ *
+ * @param msg the message accompanying the exception
+ * @param t the exception (throwable) to log
+ */
+ void debug(String msg, Throwable t);
+
+ /**
+ * Is the logger instance enabled for the INFO level?
+ *
+ * @return True if this Logger is enabled for the INFO level, false otherwise.
+ */
+ boolean isInfoEnabled();
+
+
+ /**
+ * Log a message at the INFO level.
+ *
+ * @param msg the message string to be logged
+ */
+ void info(String msg);
+
+ /**
+ * Log an exception (throwable) at the INFO level with an accompanying message.
+ *
+ * @param msg the message accompanying the exception
+ * @param t the exception (throwable) to log
+ */
+ void info(String msg, Throwable t);
+
+ /**
+ * Is the logger instance enabled for the WARN level?
+ *
+ * @return True if this Logger is enabled for the WARN level, false otherwise.
+ */
+ boolean isWarnEnabled();
+
+ /**
+ * Log a message at the WARN level.
+ *
+ * @param msg the message string to be logged
+ */
+ void warn(String msg);
+
+ /**
+ * Log an exception (throwable) at the WARN level with an accompanying message.
+ *
+ * @param msg the message accompanying the exception
+ * @param t the exception (throwable) to log
+ */
+ void warn(String msg, Throwable t);
+
+ /**
+ * Is the logger instance enabled for the ERROR level?
+ *
+ * @return True if this Logger is enabled for the ERROR level, false otherwise.
+ */
+ boolean isErrorEnabled();
+
+ /**
+ * Log a message at the ERROR level.
+ *
+ * @param msg the message string to be logged
+ */
+ void error(String msg);
+
+ /**
+ * Log an exception (throwable) at the ERROR level with an accompanying message.
+ *
+ * @param msg the message accompanying the exception
+ * @param t the exception (throwable) to log
+ */
+ void error(String msg, Throwable t);
+}
diff --git a/mongodb-crypt/src/main/com/mongodb/crypt/capi/Loggers.java b/mongodb-crypt/src/main/com/mongodb/crypt/capi/Loggers.java
new file mode 100644
index 00000000000..c57cd3994e4
--- /dev/null
+++ b/mongodb-crypt/src/main/com/mongodb/crypt/capi/Loggers.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2008-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.
+ *
+ */
+
+package com.mongodb.crypt.capi;
+
+/**
+ * This class is not part of the public API.
+ */
+public final class Loggers {
+ private static final String NAME = "org.mongodb.driver.crypt";
+
+ private static final boolean USE_SLF4J = shouldUseSLF4J();
+
+ /**
+ * @return the logger
+ */
+ public static Logger getLogger() {
+ if (USE_SLF4J) {
+ return new SLF4JLogger(NAME);
+ } else {
+ return new JULLogger(NAME);
+ }
+ }
+
+ private Loggers() {
+ }
+
+ private static boolean shouldUseSLF4J() {
+ try {
+ Class.forName("org.slf4j.Logger");
+ return true;
+ } catch (ClassNotFoundException e) {
+ return false;
+ }
+ }
+}
diff --git a/mongodb-crypt/src/main/com/mongodb/crypt/capi/MacCallback.java b/mongodb-crypt/src/main/com/mongodb/crypt/capi/MacCallback.java
new file mode 100644
index 00000000000..2ea09550bb4
--- /dev/null
+++ b/mongodb-crypt/src/main/com/mongodb/crypt/capi/MacCallback.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2008-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.
+ *
+ */
+
+package com.mongodb.crypt.capi;
+
+import com.mongodb.crypt.capi.CAPI.cstring;
+import com.mongodb.crypt.capi.CAPI.mongocrypt_binary_t;
+import com.mongodb.crypt.capi.CAPI.mongocrypt_hmac_fn;
+import com.mongodb.crypt.capi.CAPI.mongocrypt_status_t;
+import com.sun.jna.Pointer;
+
+import javax.crypto.Mac;
+import javax.crypto.spec.SecretKeySpec;
+
+import static com.mongodb.crypt.capi.CAPI.MONGOCRYPT_STATUS_ERROR_CLIENT;
+import static com.mongodb.crypt.capi.CAPI.mongocrypt_status_set;
+import static com.mongodb.crypt.capi.CAPIHelper.toByteArray;
+import static com.mongodb.crypt.capi.CAPIHelper.writeByteArrayToBinary;
+
+class MacCallback implements mongocrypt_hmac_fn {
+ private final String algorithm;
+
+ MacCallback(final String algorithm) {
+ this.algorithm = algorithm;
+ }
+
+ @Override
+ public boolean hmac(final Pointer ctx, final mongocrypt_binary_t key, final mongocrypt_binary_t in,
+ final mongocrypt_binary_t out, final mongocrypt_status_t status) {
+ try {
+ Mac mac = Mac.getInstance(algorithm);
+ SecretKeySpec keySpec = new SecretKeySpec(toByteArray(key), algorithm);
+ mac.init(keySpec);
+
+ mac.update(toByteArray(in));
+
+ byte[] result = mac.doFinal();
+ writeByteArrayToBinary(out, result);
+
+ return true;
+ } catch (Exception e) {
+ mongocrypt_status_set(status, MONGOCRYPT_STATUS_ERROR_CLIENT, 0, new cstring(e.toString()), -1);
+ return false;
+ }
+ }
+}
diff --git a/mongodb-crypt/src/main/com/mongodb/crypt/capi/MessageDigestCallback.java b/mongodb-crypt/src/main/com/mongodb/crypt/capi/MessageDigestCallback.java
new file mode 100644
index 00000000000..861290d0a8f
--- /dev/null
+++ b/mongodb-crypt/src/main/com/mongodb/crypt/capi/MessageDigestCallback.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2008-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.
+ *
+ */
+
+package com.mongodb.crypt.capi;
+
+import com.mongodb.crypt.capi.CAPI.cstring;
+import com.mongodb.crypt.capi.CAPI.mongocrypt_binary_t;
+import com.mongodb.crypt.capi.CAPI.mongocrypt_hash_fn;
+import com.mongodb.crypt.capi.CAPI.mongocrypt_status_t;
+import com.sun.jna.Pointer;
+
+import java.security.MessageDigest;
+
+import static com.mongodb.crypt.capi.CAPI.MONGOCRYPT_STATUS_ERROR_CLIENT;
+import static com.mongodb.crypt.capi.CAPI.mongocrypt_status_set;
+import static com.mongodb.crypt.capi.CAPIHelper.toByteArray;
+import static com.mongodb.crypt.capi.CAPIHelper.writeByteArrayToBinary;
+
+class MessageDigestCallback implements mongocrypt_hash_fn {
+
+ private final String algorithm;
+
+ MessageDigestCallback(final String algorithm) {
+ this.algorithm = algorithm;
+ }
+
+ @Override
+ public boolean hash(final Pointer ctx, final mongocrypt_binary_t in, final mongocrypt_binary_t out,
+ final mongocrypt_status_t status) {
+ try {
+ MessageDigest messageDigest = MessageDigest.getInstance(algorithm);
+ messageDigest.update(toByteArray(in));
+ byte[] digest = messageDigest.digest();
+ writeByteArrayToBinary(out, digest);
+ return true;
+ } catch (Exception e) {
+ mongocrypt_status_set(status, MONGOCRYPT_STATUS_ERROR_CLIENT, 0, new cstring(e.toString()), -1);
+ return false;
+ }
+ }
+}
diff --git a/mongodb-crypt/src/main/com/mongodb/crypt/capi/MongoAwsKmsProviderOptions.java b/mongodb-crypt/src/main/com/mongodb/crypt/capi/MongoAwsKmsProviderOptions.java
new file mode 100644
index 00000000000..4824197510d
--- /dev/null
+++ b/mongodb-crypt/src/main/com/mongodb/crypt/capi/MongoAwsKmsProviderOptions.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2008-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.
+ *
+ */
+
+package com.mongodb.crypt.capi;
+
+import static org.bson.assertions.Assertions.notNull;
+
+/**
+ * The options for configuring the AWS KMS provider.
+ */
+public final class MongoAwsKmsProviderOptions {
+
+ private final String accessKeyId;
+ private final String secretAccessKey;
+
+ /**
+ * Construct a builder for the options
+ *
+ * @return the builder
+ */
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * Gets the access key id
+ *
+ * @return the access key id, which may not be null
+ */
+ public String getAccessKeyId() {
+ return accessKeyId;
+ }
+
+ /**
+ * Gets the secret access key
+ *
+ * @return the secret access key, which may not be null
+ */
+ public String getSecretAccessKey() {
+ return secretAccessKey;
+ }
+
+
+ /**
+ * The builder for the options
+ */
+ public static final class Builder {
+ private String accessKeyId;
+ private String secretAccessKey;
+
+ private Builder() {
+ }
+
+ /**
+ * Sets the access key id.
+ *
+ * @param accessKeyId the access key id
+ * @return this
+ */
+ public Builder accessKeyId(final String accessKeyId) {
+ this.accessKeyId = accessKeyId;
+ return this;
+ }
+
+ /**
+ * Sets the secret access key.
+ *
+ * @param secretAccessKey the secret access key
+ * @return this
+ */
+ public Builder secretAccessKey(final String secretAccessKey) {
+ this.secretAccessKey = secretAccessKey;
+ return this;
+ }
+
+ /**
+ * Build the options.
+ *
+ * @return the options
+ */
+ public MongoAwsKmsProviderOptions build() {
+ return new MongoAwsKmsProviderOptions(this);
+ }
+ }
+
+ private MongoAwsKmsProviderOptions(final Builder builder) {
+ this.accessKeyId = notNull("AWS KMS provider accessKeyId", builder.accessKeyId);
+ this.secretAccessKey = notNull("AWS KMS provider secretAccessKey", builder.secretAccessKey);
+ }
+}
diff --git a/mongodb-crypt/src/main/com/mongodb/crypt/capi/MongoCrypt.java b/mongodb-crypt/src/main/com/mongodb/crypt/capi/MongoCrypt.java
new file mode 100644
index 00000000000..74816dbe42c
--- /dev/null
+++ b/mongodb-crypt/src/main/com/mongodb/crypt/capi/MongoCrypt.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2008-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.
+ *
+ */
+
+
+package com.mongodb.crypt.capi;
+
+import org.bson.BsonDocument;
+
+import java.io.Closeable;
+
+/**
+ * A context for encryption/decryption operations.
+ */
+public interface MongoCrypt extends Closeable {
+
+ /**
+ * Create a context to use for encryption
+ *
+ * @param database the namespace
+ * @param command the document representing the command to encrypt
+ * @return the context
+ */
+ MongoCryptContext createEncryptionContext(String database, BsonDocument command);
+
+ /**
+ * Create a context to use for decryption
+ *
+ * @param document the document to decrypt
+ * @return the context
+ */
+ MongoCryptContext createDecryptionContext(BsonDocument document);
+
+ /**
+ * Create a context to use for creating a data key
+ * @param kmsProvider the KMS provider
+ * @param options the data key options
+ * @return the context
+ */
+ MongoCryptContext createDataKeyContext(String kmsProvider, MongoDataKeyOptions options);
+
+ /**
+ * Create a context to use for encryption
+ *
+ * @param document the document to encrypt, which must be in the form { "v" : BSON value to encrypt }
+ * @param options the explicit encryption options
+ * @return the context
+ */
+ MongoCryptContext createExplicitEncryptionContext(BsonDocument document, MongoExplicitEncryptOptions options);
+
+ /**
+ * Create a context to use for encryption
+ *
+ * @param document the document to encrypt, which must be in the form { "v" : BSON value to encrypt }
+ * @param options the expression encryption options
+ * @return the context
+ * @since 1.7
+ */
+ MongoCryptContext createEncryptExpressionContext(BsonDocument document, MongoExplicitEncryptOptions options);
+
+ /**
+ * Create a context to use for encryption
+ *
+ * @param document the document to decrypt,which must be in the form { "v" : encrypted BSON value }
+ * @return the context
+ */
+ MongoCryptContext createExplicitDecryptionContext(BsonDocument document);
+
+ /**
+ * Create a context to use for encryption
+ *
+ * @param filter The filter to use for the find command on the key vault collection to retrieve datakeys to rewrap.
+ * @param options the rewrap many data key options
+ * @return the context
+ * @since 1.5
+ */
+ MongoCryptContext createRewrapManyDatakeyContext(BsonDocument filter, MongoRewrapManyDataKeyOptions options);
+
+ /**
+ * @return the version string of the loaded crypt shared dynamic library if available or null
+ * @since 1.5
+ */
+ String getCryptSharedLibVersionString();
+
+ @Override
+ void close();
+}
diff --git a/mongodb-crypt/src/main/com/mongodb/crypt/capi/MongoCryptContext.java b/mongodb-crypt/src/main/com/mongodb/crypt/capi/MongoCryptContext.java
new file mode 100644
index 00000000000..2c3aa250b87
--- /dev/null
+++ b/mongodb-crypt/src/main/com/mongodb/crypt/capi/MongoCryptContext.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright 2008-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.
+ *
+ */
+
+package com.mongodb.crypt.capi;
+
+import org.bson.BsonDocument;
+import org.bson.RawBsonDocument;
+
+import java.io.Closeable;
+
+/**
+ * An interface representing the lifecycle of an encryption or decryption request. It's modelled as a state machine.
+ */
+public interface MongoCryptContext extends Closeable {
+
+ /**
+ * The possible states.
+ */
+ enum State {
+ /**
+ * Needs collection information from the cluster encrypting to
+ */
+ NEED_MONGO_COLLINFO(CAPI.MONGOCRYPT_CTX_NEED_MONGO_COLLINFO),
+
+ /**
+ * Need to mark command with encryption markers
+ */
+ NEED_MONGO_MARKINGS(CAPI.MONGOCRYPT_CTX_NEED_MONGO_MARKINGS),
+
+ /**
+ * Need keys from the key vault
+ */
+ NEED_MONGO_KEYS(CAPI.MONGOCRYPT_CTX_NEED_MONGO_KEYS),
+
+ /**
+ * Need the key management service
+ */
+ NEED_KMS(CAPI.MONGOCRYPT_CTX_NEED_KMS),
+
+ /**
+ * Need to fetch/renew KMS credentials
+ * @since 1.4
+ */
+ NEED_KMS_CREDENTIALS(CAPI.MONGOCRYPT_CTX_NEED_KMS_CREDENTIALS),
+
+ /**
+ * Ready for encryption/decryption
+ */
+ READY(CAPI.MONGOCRYPT_CTX_READY),
+
+ /**
+ * Done
+ */
+ DONE(CAPI.MONGOCRYPT_CTX_DONE);
+
+ private final int index;
+
+ State(final int index) {
+ this.index = index;
+ }
+
+ static State fromIndex(final int index) {
+ for (State state : State.values()) {
+ if (state.index == index) {
+ return state;
+ }
+ }
+ throw new MongoCryptException("Unknown context state " + index);
+ }
+ }
+
+ /**
+ * Gets the current state.
+ *
+ * @return the current state
+ */
+ State getState();
+
+ /**
+ *
+ * @return the operation to execute
+ */
+ RawBsonDocument getMongoOperation();
+
+ /**
+ *
+ * @param document a result of the operation
+ */
+ void addMongoOperationResult(BsonDocument document);
+
+ /**
+ * Signal completion of the operation
+ */
+ void completeMongoOperation();
+
+ /**
+ * Provide KMS credentials on demand, in response to NEED_KMS_CREDENTIALS state
+ *
+ * @param credentialsDocument document containing all credentials
+ * @since 1.4
+ */
+ void provideKmsProviderCredentials(BsonDocument credentialsDocument);
+
+ /**
+ *
+ * @return the next key decryptor, or null if there are no more
+ */
+ MongoKeyDecryptor nextKeyDecryptor();
+
+ /**
+ * Indicate that all key decryptors have been completed
+ */
+ void completeKeyDecryptors();
+
+ /**
+ *
+ * @return the encrypted or decrypted document
+ */
+ RawBsonDocument finish();
+
+ @Override
+ void close();
+}
diff --git a/mongodb-crypt/src/main/com/mongodb/crypt/capi/MongoCryptContextImpl.java b/mongodb-crypt/src/main/com/mongodb/crypt/capi/MongoCryptContextImpl.java
new file mode 100644
index 00000000000..34aaafe7344
--- /dev/null
+++ b/mongodb-crypt/src/main/com/mongodb/crypt/capi/MongoCryptContextImpl.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright 2008-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.
+ *
+ */
+
+package com.mongodb.crypt.capi;
+
+import com.mongodb.crypt.capi.CAPI.mongocrypt_binary_t;
+import com.mongodb.crypt.capi.CAPI.mongocrypt_ctx_t;
+import com.mongodb.crypt.capi.CAPI.mongocrypt_kms_ctx_t;
+import org.bson.BsonDocument;
+import org.bson.RawBsonDocument;
+
+import static com.mongodb.crypt.capi.CAPI.mongocrypt_binary_destroy;
+import static com.mongodb.crypt.capi.CAPI.mongocrypt_binary_new;
+import static com.mongodb.crypt.capi.CAPI.mongocrypt_ctx_destroy;
+import static com.mongodb.crypt.capi.CAPI.mongocrypt_ctx_finalize;
+import static com.mongodb.crypt.capi.CAPI.mongocrypt_ctx_kms_done;
+import static com.mongodb.crypt.capi.CAPI.mongocrypt_ctx_mongo_done;
+import static com.mongodb.crypt.capi.CAPI.mongocrypt_ctx_mongo_feed;
+import static com.mongodb.crypt.capi.CAPI.mongocrypt_ctx_mongo_op;
+import static com.mongodb.crypt.capi.CAPI.mongocrypt_ctx_next_kms_ctx;
+import static com.mongodb.crypt.capi.CAPI.mongocrypt_ctx_provide_kms_providers;
+import static com.mongodb.crypt.capi.CAPI.mongocrypt_ctx_state;
+import static com.mongodb.crypt.capi.CAPI.mongocrypt_ctx_status;
+import static com.mongodb.crypt.capi.CAPI.mongocrypt_status_destroy;
+import static com.mongodb.crypt.capi.CAPI.mongocrypt_status_new;
+import static com.mongodb.crypt.capi.CAPI.mongocrypt_status_t;
+import static com.mongodb.crypt.capi.CAPIHelper.toBinary;
+import static com.mongodb.crypt.capi.CAPIHelper.toDocument;
+import static org.bson.assertions.Assertions.isTrue;
+import static org.bson.assertions.Assertions.notNull;
+
+class MongoCryptContextImpl implements MongoCryptContext {
+ private final mongocrypt_ctx_t wrapped;
+ private volatile boolean closed;
+
+ MongoCryptContextImpl(final mongocrypt_ctx_t wrapped) {
+ notNull("wrapped", wrapped);
+ this.wrapped = wrapped;
+ }
+
+ @Override
+ public State getState() {
+ isTrue("open", !closed);
+ return State.fromIndex(mongocrypt_ctx_state(wrapped));
+ }
+
+ @Override
+ public RawBsonDocument getMongoOperation() {
+ isTrue("open", !closed);
+ mongocrypt_binary_t binary = mongocrypt_binary_new();
+
+ try {
+ boolean success = mongocrypt_ctx_mongo_op(wrapped, binary);
+ if (!success) {
+ throwExceptionFromStatus();
+ }
+ return toDocument(binary);
+ } finally {
+ mongocrypt_binary_destroy(binary);
+ }
+ }
+
+ @Override
+ public void addMongoOperationResult(final BsonDocument document) {
+ isTrue("open", !closed);
+
+ try (BinaryHolder binaryHolder = toBinary(document)) {
+ boolean success = mongocrypt_ctx_mongo_feed(wrapped, binaryHolder.getBinary());
+ if (!success) {
+ throwExceptionFromStatus();
+ }
+ }
+ }
+
+ @Override
+ public void completeMongoOperation() {
+ isTrue("open", !closed);
+ boolean success = mongocrypt_ctx_mongo_done(wrapped);
+ if (!success) {
+ throwExceptionFromStatus();
+ }
+ }
+
+ @Override
+ public void provideKmsProviderCredentials(final BsonDocument credentialsDocument) {
+ try (BinaryHolder binaryHolder = toBinary(credentialsDocument)) {
+ boolean success = mongocrypt_ctx_provide_kms_providers(wrapped, binaryHolder.getBinary());
+ if (!success) {
+ throwExceptionFromStatus();
+ }
+ }
+ }
+
+ @Override
+ public MongoKeyDecryptor nextKeyDecryptor() {
+ isTrue("open", !closed);
+
+ mongocrypt_kms_ctx_t kmsContext = mongocrypt_ctx_next_kms_ctx(wrapped);
+ if (kmsContext == null) {
+ return null;
+ }
+ return new MongoKeyDecryptorImpl(kmsContext);
+ }
+
+ @Override
+ public void completeKeyDecryptors() {
+ isTrue("open", !closed);
+
+ boolean success = mongocrypt_ctx_kms_done(wrapped);
+ if (!success) {
+ throwExceptionFromStatus();
+ }
+
+ }
+
+ @Override
+ public RawBsonDocument finish() {
+ isTrue("open", !closed);
+
+ mongocrypt_binary_t binary = mongocrypt_binary_new();
+
+ try {
+ boolean success = mongocrypt_ctx_finalize(wrapped, binary);
+ if (!success) {
+ throwExceptionFromStatus();
+ }
+ return toDocument(binary);
+ } finally {
+ mongocrypt_binary_destroy(binary);
+ }
+ }
+
+ @Override
+ public void close() {
+ mongocrypt_ctx_destroy(wrapped);
+ closed = true;
+ }
+
+ static void throwExceptionFromStatus(final mongocrypt_ctx_t wrapped) {
+ mongocrypt_status_t status = mongocrypt_status_new();
+ mongocrypt_ctx_status(wrapped, status);
+ MongoCryptException e = new MongoCryptException(status);
+ mongocrypt_status_destroy(status);
+ throw e;
+ }
+
+ private void throwExceptionFromStatus() {
+ throwExceptionFromStatus(wrapped);
+ }
+}
diff --git a/mongodb-crypt/src/main/com/mongodb/crypt/capi/MongoCryptException.java b/mongodb-crypt/src/main/com/mongodb/crypt/capi/MongoCryptException.java
new file mode 100644
index 00000000000..63074e20bc9
--- /dev/null
+++ b/mongodb-crypt/src/main/com/mongodb/crypt/capi/MongoCryptException.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2008-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.
+ *
+ */
+
+package com.mongodb.crypt.capi;
+
+
+import com.mongodb.crypt.capi.CAPI.mongocrypt_status_t;
+
+import static com.mongodb.crypt.capi.CAPI.mongocrypt_status_code;
+import static org.bson.assertions.Assertions.isTrue;
+
+/**
+ * Top level Exception for all Mongo Crypt CAPI exceptions
+ */
+public class MongoCryptException extends RuntimeException {
+ private static final long serialVersionUID = -5524416583514807953L;
+ private final int code;
+
+ /**
+ * @param msg the message
+ */
+ public MongoCryptException(final String msg) {
+ super(msg);
+ this.code = -1;
+ }
+
+ /**
+ * @param msg the message
+ * @param cause the cause
+ */
+ public MongoCryptException(final String msg, final Throwable cause) {
+ super(msg, cause);
+ this.code = -1;
+ }
+
+ /**
+ * Construct an instance from a {@code mongocrypt_status_t}.
+ *
+ * @param status the status
+ */
+ MongoCryptException(final mongocrypt_status_t status) {
+ super(CAPI.mongocrypt_status_message(status, null).toString());
+ isTrue("status not ok", !CAPI.mongocrypt_status_ok(status));
+ code = mongocrypt_status_code(status);
+ }
+
+ /**
+ * @return the error code for the exception.
+ */
+ public int getCode() {
+ return code;
+ }
+}
diff --git a/mongodb-crypt/src/main/com/mongodb/crypt/capi/MongoCryptImpl.java b/mongodb-crypt/src/main/com/mongodb/crypt/capi/MongoCryptImpl.java
new file mode 100644
index 00000000000..2949e2a11e4
--- /dev/null
+++ b/mongodb-crypt/src/main/com/mongodb/crypt/capi/MongoCryptImpl.java
@@ -0,0 +1,423 @@
+/*
+ * Copyright 2008-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.
+ *
+ */
+
+package com.mongodb.crypt.capi;
+
+import com.mongodb.crypt.capi.CAPI.cstring;
+import com.mongodb.crypt.capi.CAPI.mongocrypt_ctx_t;
+import com.mongodb.crypt.capi.CAPI.mongocrypt_log_fn_t;
+import com.mongodb.crypt.capi.CAPI.mongocrypt_status_t;
+import com.mongodb.crypt.capi.CAPI.mongocrypt_t;
+import com.sun.jna.Pointer;
+import org.bson.BsonBinary;
+import org.bson.BsonDocument;
+import org.bson.BsonString;
+
+import javax.crypto.Cipher;
+import java.nio.ByteBuffer;
+import java.security.SecureRandom;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.function.Supplier;
+
+import static com.mongodb.crypt.capi.CAPI.MONGOCRYPT_LOG_LEVEL_ERROR;
+import static com.mongodb.crypt.capi.CAPI.MONGOCRYPT_LOG_LEVEL_FATAL;
+import static com.mongodb.crypt.capi.CAPI.MONGOCRYPT_LOG_LEVEL_INFO;
+import static com.mongodb.crypt.capi.CAPI.MONGOCRYPT_LOG_LEVEL_TRACE;
+import static com.mongodb.crypt.capi.CAPI.MONGOCRYPT_LOG_LEVEL_WARNING;
+import static com.mongodb.crypt.capi.CAPI.mongocrypt_crypt_shared_lib_version_string;
+import static com.mongodb.crypt.capi.CAPI.mongocrypt_ctx_datakey_init;
+import static com.mongodb.crypt.capi.CAPI.mongocrypt_ctx_decrypt_init;
+import static com.mongodb.crypt.capi.CAPI.mongocrypt_ctx_encrypt_init;
+import static com.mongodb.crypt.capi.CAPI.mongocrypt_ctx_explicit_decrypt_init;
+import static com.mongodb.crypt.capi.CAPI.mongocrypt_ctx_explicit_encrypt_expression_init;
+import static com.mongodb.crypt.capi.CAPI.mongocrypt_ctx_explicit_encrypt_init;
+import static com.mongodb.crypt.capi.CAPI.mongocrypt_ctx_new;
+import static com.mongodb.crypt.capi.CAPI.mongocrypt_ctx_rewrap_many_datakey_init;
+import static com.mongodb.crypt.capi.CAPI.mongocrypt_ctx_setopt_algorithm;
+import static com.mongodb.crypt.capi.CAPI.mongocrypt_ctx_setopt_algorithm_range;
+import static com.mongodb.crypt.capi.CAPI.mongocrypt_ctx_setopt_contention_factor;
+import static com.mongodb.crypt.capi.CAPI.mongocrypt_ctx_setopt_key_alt_name;
+import static com.mongodb.crypt.capi.CAPI.mongocrypt_ctx_setopt_key_encryption_key;
+import static com.mongodb.crypt.capi.CAPI.mongocrypt_ctx_setopt_key_id;
+import static com.mongodb.crypt.capi.CAPI.mongocrypt_ctx_setopt_key_material;
+import static com.mongodb.crypt.capi.CAPI.mongocrypt_ctx_setopt_query_type;
+import static com.mongodb.crypt.capi.CAPI.mongocrypt_destroy;
+import static com.mongodb.crypt.capi.CAPI.mongocrypt_init;
+import static com.mongodb.crypt.capi.CAPI.mongocrypt_is_crypto_available;
+import static com.mongodb.crypt.capi.CAPI.mongocrypt_new;
+import static com.mongodb.crypt.capi.CAPI.mongocrypt_setopt_aes_256_ctr;
+import static com.mongodb.crypt.capi.CAPI.mongocrypt_setopt_append_crypt_shared_lib_search_path;
+import static com.mongodb.crypt.capi.CAPI.mongocrypt_setopt_bypass_query_analysis;
+import static com.mongodb.crypt.capi.CAPI.mongocrypt_setopt_crypto_hook_sign_rsaes_pkcs1_v1_5;
+import static com.mongodb.crypt.capi.CAPI.mongocrypt_setopt_crypto_hooks;
+import static com.mongodb.crypt.capi.CAPI.mongocrypt_setopt_encrypted_field_config_map;
+import static com.mongodb.crypt.capi.CAPI.mongocrypt_setopt_kms_provider_aws;
+import static com.mongodb.crypt.capi.CAPI.mongocrypt_setopt_kms_provider_local;
+import static com.mongodb.crypt.capi.CAPI.mongocrypt_setopt_kms_providers;
+import static com.mongodb.crypt.capi.CAPI.mongocrypt_setopt_log_handler;
+import static com.mongodb.crypt.capi.CAPI.mongocrypt_setopt_schema_map;
+import static com.mongodb.crypt.capi.CAPI.mongocrypt_setopt_set_crypt_shared_lib_path_override;
+import static com.mongodb.crypt.capi.CAPI.mongocrypt_setopt_use_need_kms_credentials_state;
+import static com.mongodb.crypt.capi.CAPI.mongocrypt_status;
+import static com.mongodb.crypt.capi.CAPI.mongocrypt_status_destroy;
+import static com.mongodb.crypt.capi.CAPI.mongocrypt_status_new;
+import static com.mongodb.crypt.capi.CAPIHelper.toBinary;
+import static org.bson.assertions.Assertions.isTrue;
+import static org.bson.assertions.Assertions.notNull;
+
+class MongoCryptImpl implements MongoCrypt {
+ private static final Logger LOGGER = Loggers.getLogger();
+ private final mongocrypt_t wrapped;
+
+ // Keep a strong reference to all the callbacks so that they don't get garbage collected
+ @SuppressWarnings("FieldCanBeLocal")
+ private final LogCallback logCallback;
+
+ @SuppressWarnings("FieldCanBeLocal")
+ private final CipherCallback aesCBC256EncryptCallback;
+ @SuppressWarnings("FieldCanBeLocal")
+ private final CipherCallback aesCBC256DecryptCallback;
+ @SuppressWarnings("FieldCanBeLocal")
+ private final CipherCallback aesCTR256EncryptCallback;
+ @SuppressWarnings("FieldCanBeLocal")
+ private final CipherCallback aesCTR256DecryptCallback;
+ @SuppressWarnings("FieldCanBeLocal")
+ private final MacCallback hmacSha512Callback;
+ @SuppressWarnings("FieldCanBeLocal")
+ private final MacCallback hmacSha256Callback;
+ @SuppressWarnings("FieldCanBeLocal")
+ private final MessageDigestCallback sha256Callback;
+ @SuppressWarnings("FieldCanBeLocal")
+ private final SecureRandomCallback secureRandomCallback;
+ @SuppressWarnings("FieldCanBeLocal")
+ private final SigningRSAESPKCSCallback signingRSAESPKCSCallback;
+
+ private final AtomicBoolean closed;
+
+ MongoCryptImpl(final MongoCryptOptions options) {
+ closed = new AtomicBoolean();
+ wrapped = mongocrypt_new();
+ if (wrapped == null) {
+ throw new MongoCryptException("Unable to create new mongocrypt object");
+ }
+
+ logCallback = new LogCallback();
+
+ configure(() -> mongocrypt_setopt_log_handler(wrapped, logCallback, null));
+
+ if (mongocrypt_is_crypto_available()) {
+ LOGGER.debug("libmongocrypt is compiled with cryptography support, so not registering Java callbacks");
+ aesCBC256EncryptCallback = null;
+ aesCBC256DecryptCallback = null;
+ aesCTR256EncryptCallback = null;
+ aesCTR256DecryptCallback = null;
+ hmacSha512Callback = null;
+ hmacSha256Callback = null;
+ sha256Callback = null;
+ secureRandomCallback = null;
+ signingRSAESPKCSCallback = null;
+ } else {
+ LOGGER.debug("libmongocrypt is compiled without cryptography support, so registering Java callbacks");
+ // We specify NoPadding here because the underlying C library is responsible for padding prior
+ // to executing the callback
+ aesCBC256EncryptCallback = new CipherCallback("AES", "AES/CBC/NoPadding", Cipher.ENCRYPT_MODE);
+ aesCBC256DecryptCallback = new CipherCallback("AES", "AES/CBC/NoPadding", Cipher.DECRYPT_MODE);
+ aesCTR256EncryptCallback = new CipherCallback("AES", "AES/CTR/NoPadding", Cipher.ENCRYPT_MODE);
+ aesCTR256DecryptCallback = new CipherCallback("AES", "AES/CTR/NoPadding", Cipher.DECRYPT_MODE);
+
+ hmacSha512Callback = new MacCallback("HmacSHA512");
+ hmacSha256Callback = new MacCallback("HmacSHA256");
+ sha256Callback = new MessageDigestCallback("SHA-256");
+ secureRandomCallback = new SecureRandomCallback(new SecureRandom());
+
+ configure(() -> mongocrypt_setopt_crypto_hooks(wrapped, aesCBC256EncryptCallback, aesCBC256DecryptCallback,
+ secureRandomCallback, hmacSha512Callback, hmacSha256Callback,
+ sha256Callback, null));
+
+ signingRSAESPKCSCallback = new SigningRSAESPKCSCallback();
+ configure(() -> mongocrypt_setopt_crypto_hook_sign_rsaes_pkcs1_v1_5(wrapped, signingRSAESPKCSCallback, null));
+ configure(() -> mongocrypt_setopt_aes_256_ctr(wrapped, aesCTR256EncryptCallback, aesCTR256DecryptCallback, null));
+ }
+
+ if (options.getLocalKmsProviderOptions() != null) {
+ try (BinaryHolder localMasterKeyBinaryHolder = toBinary(options.getLocalKmsProviderOptions().getLocalMasterKey())) {
+ configure(() -> mongocrypt_setopt_kms_provider_local(wrapped, localMasterKeyBinaryHolder.getBinary()));
+ }
+ }
+
+ if (options.getAwsKmsProviderOptions() != null) {
+ configure(() -> mongocrypt_setopt_kms_provider_aws(wrapped,
+ new cstring(options.getAwsKmsProviderOptions().getAccessKeyId()), -1,
+ new cstring(options.getAwsKmsProviderOptions().getSecretAccessKey()), -1));
+ }
+
+ if (options.isNeedsKmsCredentialsStateEnabled()) {
+ mongocrypt_setopt_use_need_kms_credentials_state(wrapped);
+ }
+
+ if (options.getKmsProviderOptions() != null) {
+ try (BinaryHolder binaryHolder = toBinary(options.getKmsProviderOptions())) {
+ configure(() -> mongocrypt_setopt_kms_providers(wrapped, binaryHolder.getBinary()));
+ }
+ }
+
+ if (options.getLocalSchemaMap() != null) {
+ BsonDocument localSchemaMapDocument = new BsonDocument();
+ localSchemaMapDocument.putAll(options.getLocalSchemaMap());
+
+ try (BinaryHolder localSchemaMapBinaryHolder = toBinary(localSchemaMapDocument)) {
+ configure(() -> mongocrypt_setopt_schema_map(wrapped, localSchemaMapBinaryHolder.getBinary()));
+ }
+ }
+
+ if (options.isBypassQueryAnalysis()) {
+ mongocrypt_setopt_bypass_query_analysis(wrapped);
+ }
+
+ if (options.getEncryptedFieldsMap() != null) {
+ BsonDocument localEncryptedFieldsMap = new BsonDocument();
+ localEncryptedFieldsMap.putAll(options.getEncryptedFieldsMap());
+
+ try (BinaryHolder localEncryptedFieldsMapHolder = toBinary(localEncryptedFieldsMap)) {
+ configure(() -> mongocrypt_setopt_encrypted_field_config_map(wrapped, localEncryptedFieldsMapHolder.getBinary()));
+ }
+ }
+
+ options.getSearchPaths().forEach(p -> mongocrypt_setopt_append_crypt_shared_lib_search_path(wrapped, new cstring(p)));
+ if (options.getExtraOptions().containsKey("cryptSharedLibPath")) {
+ mongocrypt_setopt_set_crypt_shared_lib_path_override(wrapped, new cstring(options.getExtraOptions().getString("cryptSharedLibPath").getValue()));
+ }
+
+ configure(() -> mongocrypt_init(wrapped));
+ }
+
+ @Override
+ public MongoCryptContext createEncryptionContext(final String database, final BsonDocument commandDocument) {
+ isTrue("open", !closed.get());
+ notNull("database", database);
+ notNull("commandDocument", commandDocument);
+ mongocrypt_ctx_t context = mongocrypt_ctx_new(wrapped);
+ if (context == null) {
+ throwExceptionFromStatus();
+ }
+
+ try (BinaryHolder commandDocumentBinaryHolder = toBinary(commandDocument)) {
+ configure(() -> mongocrypt_ctx_encrypt_init(context, new cstring(database), -1,
+ commandDocumentBinaryHolder.getBinary()), context);
+ return new MongoCryptContextImpl(context);
+ }
+ }
+
+ @Override
+ public MongoCryptContext createDecryptionContext(final BsonDocument document) {
+ isTrue("open", !closed.get());
+ mongocrypt_ctx_t context = mongocrypt_ctx_new(wrapped);
+ if (context == null) {
+ throwExceptionFromStatus();
+ }
+ try (BinaryHolder documentBinaryHolder = toBinary(document)){
+ configure(() -> mongocrypt_ctx_decrypt_init(context, documentBinaryHolder.getBinary()), context);
+ }
+ return new MongoCryptContextImpl(context);
+ }
+
+ @Override
+ public MongoCryptContext createDataKeyContext(final String kmsProvider, final MongoDataKeyOptions options) {
+ isTrue("open", !closed.get());
+ mongocrypt_ctx_t context = mongocrypt_ctx_new(wrapped);
+ if (context == null) {
+ throwExceptionFromStatus();
+ }
+
+ BsonDocument keyDocument = new BsonDocument("provider", new BsonString(kmsProvider));
+ BsonDocument masterKey = options.getMasterKey();
+ if (masterKey != null) {
+ masterKey.forEach(keyDocument::append);
+ }
+ try (BinaryHolder masterKeyHolder = toBinary(keyDocument)) {
+ configure(() -> mongocrypt_ctx_setopt_key_encryption_key(context, masterKeyHolder.getBinary()), context);
+ }
+
+ if (options.getKeyAltNames() != null) {
+ for (String cur : options.getKeyAltNames()) {
+ try (BinaryHolder keyAltNameBinaryHolder = toBinary(new BsonDocument("keyAltName", new BsonString(cur)))) {
+ configure(() -> mongocrypt_ctx_setopt_key_alt_name(context, keyAltNameBinaryHolder.getBinary()), context);
+ }
+ }
+ }
+
+ if (options.getKeyMaterial() != null) {
+ try (BinaryHolder keyMaterialBinaryHolder = toBinary(new BsonDocument("keyMaterial", new BsonBinary(options.getKeyMaterial())))) {
+ configure(() -> mongocrypt_ctx_setopt_key_material(context, keyMaterialBinaryHolder.getBinary()), context);
+ }
+ }
+
+ if (!mongocrypt_ctx_datakey_init(context)) {
+ MongoCryptContextImpl.throwExceptionFromStatus(context);
+ }
+ return new MongoCryptContextImpl(context);
+ }
+
+ @Override
+ public MongoCryptContext createExplicitEncryptionContext(final BsonDocument document, final MongoExplicitEncryptOptions options) {
+ isTrue("open", !closed.get());
+ mongocrypt_ctx_t context = configureExplicitEncryption(options);
+
+ try (BinaryHolder documentBinaryHolder = toBinary(document)) {
+ configure(() -> mongocrypt_ctx_explicit_encrypt_init(context, documentBinaryHolder.getBinary()), context);
+ }
+
+ return new MongoCryptContextImpl(context);
+ }
+
+ @Override
+ public MongoCryptContext createEncryptExpressionContext(final BsonDocument document, final MongoExplicitEncryptOptions options) {
+ isTrue("open", !closed.get());
+ mongocrypt_ctx_t context = configureExplicitEncryption(options);
+
+ try (BinaryHolder documentBinaryHolder = toBinary(document)) {
+ configure(() -> mongocrypt_ctx_explicit_encrypt_expression_init(context, documentBinaryHolder.getBinary()), context);
+ }
+ return new MongoCryptContextImpl(context);
+ }
+
+ @Override
+ public MongoCryptContext createExplicitDecryptionContext(final BsonDocument document) {
+ isTrue("open", !closed.get());
+ mongocrypt_ctx_t context = mongocrypt_ctx_new(wrapped);
+ if (context == null) {
+ throwExceptionFromStatus();
+ }
+ try (BinaryHolder binaryHolder = toBinary(document)) {
+ configure(() -> mongocrypt_ctx_explicit_decrypt_init(context, binaryHolder.getBinary()), context);
+ }
+ return new MongoCryptContextImpl(context);
+ }
+
+ @Override
+ public MongoCryptContext createRewrapManyDatakeyContext(final BsonDocument filter, final MongoRewrapManyDataKeyOptions options) {
+ isTrue("open", !closed.get());
+ mongocrypt_ctx_t context = mongocrypt_ctx_new(wrapped);
+ if (context == null) {
+ throwExceptionFromStatus();
+ }
+
+ if (options != null && options.getProvider() != null) {
+ BsonDocument keyDocument = new BsonDocument("provider", new BsonString(options.getProvider()));
+ BsonDocument masterKey = options.getMasterKey();
+ if (masterKey != null) {
+ masterKey.forEach(keyDocument::append);
+ }
+ try (BinaryHolder binaryHolder = toBinary(keyDocument)) {
+ configure(() -> mongocrypt_ctx_setopt_key_encryption_key(context, binaryHolder.getBinary()), context);
+ }
+ }
+
+ try (BinaryHolder binaryHolder = toBinary(filter)) {
+ configure(() -> mongocrypt_ctx_rewrap_many_datakey_init(context, binaryHolder.getBinary()), context);
+ }
+ return new MongoCryptContextImpl(context);
+ }
+
+ @Override
+ public String getCryptSharedLibVersionString() {
+ cstring versionString = mongocrypt_crypt_shared_lib_version_string(wrapped, null);
+ return versionString == null ? null : versionString.toString();
+ }
+
+ @Override
+ public void close() {
+ if (!closed.getAndSet(true)) {
+ mongocrypt_destroy(wrapped);
+ }
+ }
+
+ private mongocrypt_ctx_t configureExplicitEncryption(final MongoExplicitEncryptOptions options) {
+ mongocrypt_ctx_t context = mongocrypt_ctx_new(wrapped);
+ if (context == null) {
+ throwExceptionFromStatus();
+ }
+
+ if (options.getKeyId() != null) {
+ try (BinaryHolder keyIdBinaryHolder = toBinary(ByteBuffer.wrap(options.getKeyId().getData()))) {
+ configure(() -> mongocrypt_ctx_setopt_key_id(context, keyIdBinaryHolder.getBinary()), context);
+ }
+ } else if (options.getKeyAltName() != null) {
+ try (BinaryHolder keyAltNameBinaryHolder = toBinary(new BsonDocument("keyAltName", new BsonString(options.getKeyAltName())))) {
+ configure(() -> mongocrypt_ctx_setopt_key_alt_name(context, keyAltNameBinaryHolder.getBinary()), context);
+ }
+ }
+
+ if (options.getAlgorithm() != null) {
+ configure(() -> mongocrypt_ctx_setopt_algorithm(context, new cstring(options.getAlgorithm()), -1), context);
+ }
+ if (options.getQueryType() != null) {
+ configure(() -> mongocrypt_ctx_setopt_query_type(context, new cstring(options.getQueryType()), -1), context);
+ }
+ if (options.getContentionFactor() != null) {
+ configure(() -> mongocrypt_ctx_setopt_contention_factor(context, options.getContentionFactor()), context);
+ }
+ if (options.getRangeOptions() != null) {
+ try (BinaryHolder rangeOptionsHolder = toBinary(options.getRangeOptions())) {
+ configure(() -> mongocrypt_ctx_setopt_algorithm_range(context, rangeOptionsHolder.getBinary()), context);
+ }
+ }
+ return context;
+ }
+
+
+ private void configure(final Supplier