From 77f99cc5caa519116daab3e850fd1b468b0fa971 Mon Sep 17 00:00:00 2001 From: Ross Lawley Date: Wed, 29 Apr 2026 17:49:06 +0100 Subject: [PATCH 1/2] Retry KMS requests on transient errors Add libmongocrypt CAPI bindings for KMS retry support and wire retry logic through the sync and reactive driver stacks. Transient KMS HTTP and network errors are retried with backoff delays managed by libmongocrypt; retry is enabled unconditionally. - Add native bindings: mongocrypt_setopt_retry_kms, mongocrypt_kms_ctx_usleep, mongocrypt_kms_ctx_feed_with_retry, mongocrypt_kms_ctx_fail - Add sleepMicroseconds(), feedAndRetry(), fail() to MongoKeyDecryptor - Enable KMS retry unconditionally in MongoCryptImpl - Rewrite sync Crypt.decryptKey() with retry loop, timeout-aware - Add retry logic to reactive KeyManagementService.decryptKey() - Fix TlsChannelImpl.read() to preserve bytes delivered alongside close_notify (already fixed upstream in marianobarrios/tls-channel) - Add spec Section 24 KMS retry integration tests (sync + reactive) - Add Evergreen CI task for KMS retry tests JAVA-5391 --- .evergreen/.evg.yml | 27 ++ .evergreen/run-kms-retry-tests.sh | 44 +++ .../tlschannel/impl/TlsChannelImpl.java | 7 +- .../internal/crypt/KeyManagementService.java | 148 ++++++--- ...ClientSideEncryptionKmsRetryProseTest.java | 30 ++ .../com/mongodb/client/internal/Crypt.java | 89 +++++- ...ClientSideEncryptionKmsRetryProseTest.java | 282 ++++++++++++++++++ ...ClientSideEncryptionKmsRetryProseTest.java | 28 ++ .../com/mongodb/internal/crypt/capi/CAPI.java | 57 ++++ .../internal/crypt/capi/MongoCryptImpl.java | 3 + .../crypt/capi/MongoKeyDecryptor.java | 34 +++ .../crypt/capi/MongoKeyDecryptorImpl.java | 32 ++ 12 files changed, 726 insertions(+), 55 deletions(-) create mode 100755 .evergreen/run-kms-retry-tests.sh create mode 100644 driver-reactive-streams/src/test/functional/com/mongodb/reactivestreams/client/ClientSideEncryptionKmsRetryProseTest.java create mode 100644 driver-sync/src/test/functional/com/mongodb/client/AbstractClientSideEncryptionKmsRetryProseTest.java create mode 100644 driver-sync/src/test/functional/com/mongodb/client/ClientSideEncryptionKmsRetryProseTest.java diff --git a/.evergreen/.evg.yml b/.evergreen/.evg.yml index 7de253baac6..b0772260acd 100644 --- a/.evergreen/.evg.yml +++ b/.evergreen/.evg.yml @@ -764,6 +764,16 @@ functions: set +o xtrace MONGODB_URI="${MONGODB_URI}" KMS_TLS_ERROR_TYPE=${KMS_TLS_ERROR_TYPE} .evergreen/run-kms-tls-tests.sh + "run-kms-retry-test": + - command: shell.exec + type: "test" + params: + working_dir: "src" + script: | + ${PREPARE_SHELL} + set +o xtrace + MONGODB_URI="${MONGODB_URI}" .evergreen/run-kms-retry-tests.sh + "run-csfle-aws-from-environment-test": - command: shell.exec type: "test" @@ -1632,6 +1642,17 @@ tasks: AUTH: "noauth" SSL: "nossl" + - name: "test-kms-retry-task" + tags: [ "kms-retry" ] + commands: + - func: "start-mongo-orchestration" + vars: + TOPOLOGY: "server" + AUTH: "noauth" + SSL: "nossl" + - func: "start-csfle-servers" + - func: "run-kms-retry-test" + - name: "test-csfle-aws-from-environment-task" tags: [ "csfle-aws-from-environment" ] commands: @@ -2528,6 +2549,12 @@ buildvariants: tasks: - name: ".kms-tls" + - matrix_name: "kms-retry-test" + matrix_spec: { os: "linux", version: [ "5.0" ], topology: [ "standalone" ] } + display_name: "CSFLE KMS Retry" + tasks: + - name: ".kms-retry" + - matrix_name: "csfle-aws-from-environment-test" matrix_spec: { os: "linux", version: [ "5.0" ], topology: [ "standalone" ] } display_name: "CSFLE AWS From Environment" diff --git a/.evergreen/run-kms-retry-tests.sh b/.evergreen/run-kms-retry-tests.sh new file mode 100755 index 00000000000..c9a96ddafa2 --- /dev/null +++ b/.evergreen/run-kms-retry-tests.sh @@ -0,0 +1,44 @@ +#!/bin/bash + +# Don't trace since the URI contains a password that shouldn't show up in the logs +set -o errexit # Exit the script with error if any of the commands fail + +# Supported/used environment variables: +# MONGODB_URI Set the suggested connection MONGODB_URI (including credentials and topology info) + +############################################ +# Main Program # +############################################ +RELATIVE_DIR_PATH="$(dirname "${BASH_SOURCE:-$0}")" +. "${RELATIVE_DIR_PATH}/setup-env.bash" +echo "Running KMS Retry tests" + +cp ${JAVA_HOME}/lib/security/cacerts mongo-truststore +${JAVA_HOME}/bin/keytool -importcert -trustcacerts -file ${DRIVERS_TOOLS}/.evergreen/x509gen/ca.pem -keystore mongo-truststore -storepass changeit -storetype JKS -noprompt + +export GRADLE_EXTRA_VARS="-Pssl.enabled=true -Pssl.trustStoreType=jks -Pssl.trustStore=`pwd`/mongo-truststore -Pssl.trustStorePassword=changeit" + +./gradlew -version + +# Disable errexit so both suites run and their exit codes can be captured below. +set +o errexit + +./gradlew --stacktrace --info ${GRADLE_EXTRA_VARS} -Dorg.mongodb.test.uri=${MONGODB_URI} \ + -Dorg.mongodb.test.kms.retry.ca.path="${DRIVERS_TOOLS}/.evergreen/x509gen/ca.pem" \ + driver-sync:cleanTest driver-sync:test --tests ClientSideEncryptionKmsRetryProseTest +first=$? +echo $first + +./gradlew --stacktrace --info ${GRADLE_EXTRA_VARS} -Dorg.mongodb.test.uri=${MONGODB_URI} \ + -Dorg.mongodb.test.kms.retry.ca.path="${DRIVERS_TOOLS}/.evergreen/x509gen/ca.pem" \ + driver-reactive-streams:cleanTest driver-reactive-streams:test --tests ClientSideEncryptionKmsRetryProseTest +second=$? +echo $second + +if [ $first -ne 0 ]; then + exit $first +elif [ $second -ne 0 ]; then + exit $second +else + exit 0 +fi diff --git a/driver-core/src/main/com/mongodb/internal/connection/tlschannel/impl/TlsChannelImpl.java b/driver-core/src/main/com/mongodb/internal/connection/tlschannel/impl/TlsChannelImpl.java index 20bc69e81f0..2daec943e5d 100644 --- a/driver-core/src/main/com/mongodb/internal/connection/tlschannel/impl/TlsChannelImpl.java +++ b/driver-core/src/main/com/mongodb/internal/connection/tlschannel/impl/TlsChannelImpl.java @@ -236,10 +236,13 @@ public long read(ByteBufferSet dest) throws IOException { case NOT_HANDSHAKING: case FINISHED: UnwrapResult res = readAndUnwrap(Optional.of(dest)); + bytesToReturn = res.bytesProduced; if (res.wasClosed) { - return -1; + // JAVA-5391: return any bytes produced alongside close_notify; the next read + // sees shutdownReceived and returns -1. Fixed in upstream marianobarrios/tls-channel; + // this is the minimal patch until the vendored snapshot is refreshed. + return bytesToReturn > 0 ? bytesToReturn : -1; } - bytesToReturn = res.bytesProduced; handshakeStatus = res.lastHandshakeStatus; break; case NEED_TASK: diff --git a/driver-reactive-streams/src/main/com/mongodb/reactivestreams/client/internal/crypt/KeyManagementService.java b/driver-reactive-streams/src/main/com/mongodb/reactivestreams/client/internal/crypt/KeyManagementService.java index 67ebf421c9c..076c8743f1b 100644 --- a/driver-reactive-streams/src/main/com/mongodb/reactivestreams/client/internal/crypt/KeyManagementService.java +++ b/driver-reactive-streams/src/main/com/mongodb/reactivestreams/client/internal/crypt/KeyManagementService.java @@ -48,10 +48,12 @@ import java.io.Closeable; import java.nio.channels.CompletionHandler; import java.nio.channels.InterruptedByTimeoutException; +import java.time.Duration; import java.util.List; import java.util.Map; import static java.util.Collections.singletonList; +import static java.util.concurrent.TimeUnit.MICROSECONDS; import static java.util.concurrent.TimeUnit.MILLISECONDS; import static org.bson.assertions.Assertions.assertTrue; @@ -74,6 +76,29 @@ public void close() { } Mono decryptKey(final MongoKeyDecryptor keyDecryptor, @Nullable final Timeout operationTimeout) { + return Mono.defer(() -> { + long sleepMicros = keyDecryptor.sleepMicroseconds(); + if (sleepMicros > 0 && operationTimeout != null) { + operationTimeout.run(MICROSECONDS, + () -> { }, + remainingMicros -> { + if (remainingMicros < sleepMicros) { + throw TimeoutContext.createMongoTimeoutException(TIMEOUT_ERROR_MESSAGE); + } + }, + () -> { + throw TimeoutContext.createMongoTimeoutException(TIMEOUT_ERROR_MESSAGE); + }); + } + Mono attempt = sleepMicros > 0 + ? Mono.delay(Duration.ofNanos(MICROSECONDS.toNanos(sleepMicros))) + .then(attemptDecryptKey(keyDecryptor, operationTimeout)) + : attemptDecryptKey(keyDecryptor, operationTimeout); + return attempt; + }).onErrorMap(this::unWrapException); + } + + private Mono attemptDecryptKey(final MongoKeyDecryptor keyDecryptor, @Nullable final Timeout operationTimeout) { SocketSettings socketSettings = SocketSettings.builder() .connectTimeout(timeoutMillis, MILLISECONDS) .readTimeout(timeoutMillis, MILLISECONDS) @@ -85,84 +110,119 @@ Mono decryptKey(final MongoKeyDecryptor keyDecryptor, @Nullable final Time LOGGER.info("Connecting to KMS server at " + serverAddress); - return Mono.create(sink -> { - Stream stream = streamFactory.create(serverAddress); + return Mono.create(sink -> { OperationContext operationContext = createOperationContext(operationTimeout, socketSettings); + Stream stream = streamFactory.create(serverAddress); stream.openAsync(operationContext, new AsyncCompletionHandler() { @Override public void completed(@Nullable final Void ignored) { - streamWrite(stream, keyDecryptor, operationContext, sink); + try { + streamWrite(stream, keyDecryptor, operationContext, sink); + } catch (Throwable t) { + stream.close(); + sink.error(t); + } } @Override public void failed(final Throwable t) { stream.close(); - handleError(t, operationContext, sink); + failOrHandleError(t, keyDecryptor, operationContext, sink); } }); - }).onErrorMap(this::unWrapException); + }).flatMap(shouldRetry -> { + if (shouldRetry) { + return decryptKey(keyDecryptor, operationTimeout); + } + return Mono.empty(); + }); } private void streamWrite(final Stream stream, final MongoKeyDecryptor keyDecryptor, - final OperationContext operationContext, final MonoSink sink) { + final OperationContext operationContext, final MonoSink sink) { List byteBufs = singletonList(new ByteBufNIO(keyDecryptor.getMessage())); stream.writeAsync(byteBufs, operationContext, new AsyncCompletionHandler() { @Override public void completed(@Nullable final Void aVoid) { - streamRead(stream, keyDecryptor, operationContext, sink); + try { + int bytesNeeded = keyDecryptor.bytesNeeded(); + int readSize = bytesNeeded > 0 ? bytesNeeded : MongoKeyDecryptor.DEFAULT_KMS_READ_SIZE; + streamRead(stream, keyDecryptor, operationContext, sink, readSize); + } catch (Throwable t) { + stream.close(); + sink.error(t); + } } @Override public void failed(final Throwable t) { stream.close(); - handleError(t, operationContext, sink); + failOrHandleError(t, keyDecryptor, operationContext, sink); } }); } private void streamRead(final Stream stream, final MongoKeyDecryptor keyDecryptor, - final OperationContext operationContext, final MonoSink sink) { - int bytesNeeded = keyDecryptor.bytesNeeded(); - if (bytesNeeded > 0) { - AsynchronousChannelStream asyncStream = (AsynchronousChannelStream) stream; - ByteBuf buffer = asyncStream.getBuffer(bytesNeeded); - long readTimeoutMS = operationContext.getTimeoutContext().getReadTimeoutMS(); - asyncStream.getChannel().read(buffer.asNIO(), readTimeoutMS, MILLISECONDS, null, - new CompletionHandler() { - - @Override - public void completed(final Integer integer, final Void aVoid) { - if (integer == -1) { - sink.error(new MongoException( - "Unexpected end of stream from KMS provider " + keyDecryptor.getKmsProvider())); - return; - } - buffer.flip(); - try { - keyDecryptor.feed(buffer.asNIO()); - buffer.release(); - streamRead(stream, keyDecryptor, operationContext, sink); - } catch (Throwable t) { - sink.error(t); - } - } - - @Override - public void failed(final Throwable t, final Void aVoid) { - buffer.release(); - stream.close(); - handleError(t, operationContext, sink); - } - }); - } else { + final OperationContext operationContext, final MonoSink sink, + final int readSize) { + if (readSize <= 0) { stream.close(); - sink.success(); + sink.success(false); + return; } + AsynchronousChannelStream asyncStream = (AsynchronousChannelStream) stream; + ByteBuf buffer = asyncStream.getBuffer(readSize); + long readTimeoutMS = operationContext.getTimeoutContext().getReadTimeoutMS(); + asyncStream.getChannel().read(buffer.asNIO(), readTimeoutMS, MILLISECONDS, null, + new CompletionHandler() { + + @Override + public void completed(final Integer integer, final Void aVoid) { + try { + if (integer == -1) { + buffer.release(); + stream.close(); + MongoException eof = new MongoException("Unexpected end of stream from KMS provider " + + keyDecryptor.getKmsProvider()); + failOrHandleError(eof, keyDecryptor, operationContext, sink); + return; + } + buffer.flip(); + boolean shouldRetry; + try { + shouldRetry = keyDecryptor.feedAndRetry(buffer.asNIO()); + } finally { + buffer.release(); + } + if (shouldRetry) { + stream.close(); + sink.success(true); + } else { + streamRead(stream, keyDecryptor, operationContext, sink, + keyDecryptor.bytesNeeded()); + } + } catch (Throwable t) { + stream.close(); + sink.error(t); + } + } + + @Override + public void failed(final Throwable t, final Void aVoid) { + buffer.release(); + stream.close(); + failOrHandleError(t, keyDecryptor, operationContext, sink); + } + }); } - private static void handleError(final Throwable t, final OperationContext operationContext, final MonoSink sink) { + private static void failOrHandleError(final Throwable t, final MongoKeyDecryptor keyDecryptor, + final OperationContext operationContext, final MonoSink sink) { if (isTimeoutException(t) && operationContext.getTimeoutContext().hasTimeoutMS()) { sink.error(TimeoutContext.createMongoTimeoutException(TIMEOUT_ERROR_MESSAGE, t)); + } else if (keyDecryptor.fail()) { + LOGGER.debug("Retrying KMS request after transient error", t); + sink.success(true); } else { sink.error(t); } diff --git a/driver-reactive-streams/src/test/functional/com/mongodb/reactivestreams/client/ClientSideEncryptionKmsRetryProseTest.java b/driver-reactive-streams/src/test/functional/com/mongodb/reactivestreams/client/ClientSideEncryptionKmsRetryProseTest.java new file mode 100644 index 00000000000..1dd4f0f833d --- /dev/null +++ b/driver-reactive-streams/src/test/functional/com/mongodb/reactivestreams/client/ClientSideEncryptionKmsRetryProseTest.java @@ -0,0 +1,30 @@ +/* + * 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.reactivestreams.client; + +import com.mongodb.ClientEncryptionSettings; +import com.mongodb.client.AbstractClientSideEncryptionKmsRetryProseTest; +import com.mongodb.client.vault.ClientEncryption; +import com.mongodb.reactivestreams.client.syncadapter.SyncClientEncryption; +import com.mongodb.reactivestreams.client.vault.ClientEncryptions; + +public class ClientSideEncryptionKmsRetryProseTest extends AbstractClientSideEncryptionKmsRetryProseTest { + @Override + public ClientEncryption getClientEncryption(final ClientEncryptionSettings settings) { + return new SyncClientEncryption(ClientEncryptions.create(settings)); + } +} diff --git a/driver-sync/src/main/com/mongodb/client/internal/Crypt.java b/driver-sync/src/main/com/mongodb/client/internal/Crypt.java index 67fac13770c..6039d64ae98 100644 --- a/driver-sync/src/main/com/mongodb/client/internal/Crypt.java +++ b/driver-sync/src/main/com/mongodb/client/internal/Crypt.java @@ -24,8 +24,11 @@ import com.mongodb.client.model.vault.EncryptOptions; import com.mongodb.client.model.vault.RewrapManyDataKeyOptions; import com.mongodb.crypt.capi.MongoCryptException; +import com.mongodb.internal.TimeoutContext; import com.mongodb.internal.capi.MongoCryptHelper; import com.mongodb.internal.crypt.capi.MongoCrypt; +import com.mongodb.internal.diagnostics.logging.Logger; +import com.mongodb.internal.diagnostics.logging.Loggers; import com.mongodb.internal.crypt.capi.MongoCryptContext; import com.mongodb.internal.crypt.capi.MongoDataKeyOptions; import com.mongodb.internal.crypt.capi.MongoKeyDecryptor; @@ -38,11 +41,13 @@ import org.bson.RawBsonDocument; import java.io.Closeable; +import java.io.EOFException; import java.io.IOException; import java.io.InputStream; import java.nio.ByteBuffer; import java.util.List; import java.util.Map; +import java.util.concurrent.TimeUnit; import java.util.function.Supplier; import static com.mongodb.assertions.Assertions.assertNotNull; @@ -56,6 +61,8 @@ */ public class Crypt implements Closeable { + private static final Logger LOGGER = Loggers.getLogger("client"); + private static final String TIMEOUT_ERROR_MESSAGE = "KMS key decryption exceeded the timeout limit."; private static final RawBsonDocument EMPTY_RAW_BSON_DOCUMENT = RawBsonDocument.parse("{}"); private final MongoCrypt mongoCrypt; private final Map> kmsProviders; @@ -361,19 +368,83 @@ private void decryptKeys(final MongoCryptContext cryptContext, @Nullable final T } } - private void decryptKey(final MongoKeyDecryptor keyDecryptor, @Nullable final Timeout operationTimeout) throws IOException { - try (InputStream inputStream = keyManagementService.stream(keyDecryptor.getKmsProvider(), keyDecryptor.getHostName(), - keyDecryptor.getMessage(), operationTimeout)) { - int bytesNeeded = keyDecryptor.bytesNeeded(); + private void decryptKey(final MongoKeyDecryptor keyDecryptor, @Nullable final Timeout operationTimeout) + throws IOException, InterruptedException { + while (true) { + long sleepMicros = keyDecryptor.sleepMicroseconds(); + if (sleepMicros > 0) { + if (operationTimeout != null) { + operationTimeout.run(TimeUnit.MICROSECONDS, + () -> { }, + remainingMicros -> { + if (remainingMicros < sleepMicros) { + throw TimeoutContext.createMongoTimeoutException( + TIMEOUT_ERROR_MESSAGE); + } + }, + () -> { + throw TimeoutContext.createMongoTimeoutException( + TIMEOUT_ERROR_MESSAGE); + }); + } + TimeUnit.MICROSECONDS.sleep(sleepMicros); + } + boolean shouldRetry; + try { + shouldRetry = attemptDecryptKey(keyDecryptor, operationTimeout); + } catch (IOException e) { + if (!keyDecryptor.fail()) { + throw e; + } + LOGGER.debug("Retrying KMS request after transient error", e); + continue; + } + if (!shouldRetry) { + return; + } + } + } - while (bytesNeeded > 0) { - byte[] bytes = new byte[bytesNeeded]; + private boolean attemptDecryptKey(final MongoKeyDecryptor keyDecryptor, @Nullable final Timeout operationTimeout) + throws IOException { + Timeout.onExistsAndExpired(operationTimeout, () -> { + throw TimeoutContext.createMongoTimeoutException(TIMEOUT_ERROR_MESSAGE); + }); + // After a fail()-triggered retry, bytesNeeded() may return 0 until feedAndRetry() clears + // the flag, so the do-while guarantees at least one read using DEFAULT_KMS_READ_SIZE. + InputStream inputStream = keyManagementService.stream(keyDecryptor.getKmsProvider(), keyDecryptor.getHostName(), + keyDecryptor.getMessage(), operationTimeout); + Throwable primary = null; + try { + int bytesNeeded = keyDecryptor.bytesNeeded(); + int readSize = bytesNeeded > 0 ? bytesNeeded : MongoKeyDecryptor.DEFAULT_KMS_READ_SIZE; + do { + byte[] bytes = new byte[readSize]; int bytesRead = inputStream.read(bytes, 0, bytes.length); if (bytesRead == -1) { - throw new MongoException("Unexpected end of stream from KMS provider " + keyDecryptor.getKmsProvider()); + throw new EOFException("Unexpected end of stream from KMS provider " + keyDecryptor.getKmsProvider()); + } + if (keyDecryptor.feedAndRetry(ByteBuffer.wrap(bytes, 0, bytesRead))) { + return true; + } + readSize = keyDecryptor.bytesNeeded(); + } while (readSize > 0); + return false; + } catch (Throwable t) { + primary = t; + throw t; + } finally { + // If the feed loop succeeded, suppress close() failures so they do not trigger a retry + // of an already-complete KMS exchange. If the feed loop threw, preserve the primary + // exception and attach any close() failure as suppressed — matching try-with-resources. + try { + inputStream.close(); + } catch (IOException closeException) { + if (primary != null) { + primary.addSuppressed(closeException); + } else { + LOGGER.debug("Ignoring close() failure after successful KMS exchange", closeException); } - keyDecryptor.feed(ByteBuffer.wrap(bytes, 0, bytesRead)); - bytesNeeded = keyDecryptor.bytesNeeded(); } } } diff --git a/driver-sync/src/test/functional/com/mongodb/client/AbstractClientSideEncryptionKmsRetryProseTest.java b/driver-sync/src/test/functional/com/mongodb/client/AbstractClientSideEncryptionKmsRetryProseTest.java new file mode 100644 index 00000000000..e7aa620d8dd --- /dev/null +++ b/driver-sync/src/test/functional/com/mongodb/client/AbstractClientSideEncryptionKmsRetryProseTest.java @@ -0,0 +1,282 @@ +/* + * 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.client; + +import com.mongodb.ClientEncryptionSettings; +import com.mongodb.MongoClientException; +import com.mongodb.MongoOperationTimeoutException; +import com.mongodb.client.model.vault.DataKeyOptions; +import com.mongodb.client.model.vault.EncryptOptions; +import com.mongodb.client.vault.ClientEncryption; +import com.mongodb.lang.NonNull; +import com.mongodb.lang.Nullable; +import org.bson.BsonBinary; +import org.bson.BsonDocument; +import org.bson.BsonInt32; +import org.bson.BsonString; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import javax.net.ssl.HttpsURLConnection; +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManagerFactory; +import java.io.FileInputStream; +import java.io.OutputStream; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.security.KeyStore; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +import static com.mongodb.ClusterFixture.getEnv; +import static com.mongodb.ClusterFixture.hasEncryptionTestsEnabled; +import static com.mongodb.ClusterFixture.serverVersionAtLeast; +import static com.mongodb.client.Fixture.getMongoClientSettings; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assumptions.assumeTrue; + +/** + * See + * 24. KMS Retry Tests. + * + *

Requires the {@code org.mongodb.test.kms.retry.ca.path} system property pointing to the CA cert for the + * failpoint server. + */ +public abstract class AbstractClientSideEncryptionKmsRetryProseTest { + + private static final String FAILPOINT_SERVER_ADDRESS = "127.0.0.1:9003"; + private static final String FAILPOINT_URL_BASE = "https://" + FAILPOINT_SERVER_ADDRESS; + + @NonNull + protected abstract ClientEncryption getClientEncryption(ClientEncryptionSettings settings); + + @BeforeEach + public void setUp() { + assumeTrue(System.getProperty("org.mongodb.test.kms.retry.ca.path") != null, + "org.mongodb.test.kms.retry.ca.path system property is not set"); + } + + /** + * Case 1: createDataKey and encrypt with TCP retry. + */ + @ParameterizedTest(name = "Case 1: TCP retry with {0}") + @ValueSource(strings = {"aws", "azure", "gcp"}) + public void testCreateDataKeyAndEncryptWithTcpRetry(final String provider) { + assumeTrue(hasEncryptionTestsEnabled()); + assumeTrue(serverVersionAtLeast(4, 2)); + + try (ClientEncryption clientEncryption = createClientEncryptionForRetryTest()) { + setFailpoint("network", 1); + BsonBinary keyId = assertDoesNotThrow( + () -> clientEncryption.createDataKey(provider, getDataKeyOptions(provider))); + + setFailpoint("network", 1); + assertDoesNotThrow( + () -> clientEncryption.encrypt(new BsonInt32(123), + new EncryptOptions("AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic").keyId(keyId))); + } + } + + /** + * Case 2: createDataKey and encrypt with HTTP retry. + */ + @ParameterizedTest(name = "Case 2: HTTP retry with {0}") + @ValueSource(strings = {"aws", "azure", "gcp"}) + public void testCreateDataKeyAndEncryptWithHttpRetry(final String provider) { + assumeTrue(hasEncryptionTestsEnabled()); + assumeTrue(serverVersionAtLeast(4, 2)); + + try (ClientEncryption clientEncryption = createClientEncryptionForRetryTest()) { + setFailpoint("http", 1); + BsonBinary keyId = assertDoesNotThrow( + () -> clientEncryption.createDataKey(provider, getDataKeyOptions(provider))); + + setFailpoint("http", 1); + assertDoesNotThrow( + () -> clientEncryption.encrypt(new BsonInt32(123), + new EncryptOptions("AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic").keyId(keyId))); + } + } + + /** + * Case 3: createDataKey fails after too many retries. + */ + @ParameterizedTest(name = "Case 3: Exhausted retries with {0}") + @ValueSource(strings = {"aws", "azure", "gcp"}) + public void testCreateDataKeyFailsAfterTooManyRetries(final String provider) { + assumeTrue(hasEncryptionTestsEnabled()); + assumeTrue(serverVersionAtLeast(4, 2)); + + try (ClientEncryption clientEncryption = createClientEncryptionForRetryTest()) { + setFailpoint("network", 4); + assertThrows(MongoClientException.class, + () -> clientEncryption.createDataKey(provider, getDataKeyOptions(provider))); + } + } + + /** + * Prose test: createDataKey surfaces MongoOperationTimeoutException when the operation timeout expires + * mid-retry. Configures a 100ms operation timeout and a failpoint that triggers repeated network errors; + * the cumulative retry backoff must push the operation past its deadline, and the expiry check at the + * top of each retry iteration must surface MongoOperationTimeoutException rather than MongoClientException. + */ + @Test + public void testCreateDataKeyTimesOutDuringRetry() { + assumeTrue(hasEncryptionTestsEnabled()); + assumeTrue(serverVersionAtLeast(4, 2)); + + try (ClientEncryption clientEncryption = createClientEncryptionForRetryTest(100L)) { + setFailpoint("network", 4); + assertThrows(MongoOperationTimeoutException.class, + () -> clientEncryption.createDataKey("aws", getDataKeyOptions("aws"))); + } + } + + private ClientEncryption createClientEncryptionForRetryTest() { + return createClientEncryptionForRetryTest(null); + } + + private ClientEncryption createClientEncryptionForRetryTest(@Nullable final Long timeoutMS) { + Map> kmsProviders = getKmsProvidersForRetryTest(); + SSLContext failpointSslContext = createFailpointSslContext(); + Map kmsProviderSslContextMap = new HashMap<>(); + kmsProviderSslContextMap.put("aws", failpointSslContext); + kmsProviderSslContextMap.put("azure", failpointSslContext); + kmsProviderSslContextMap.put("gcp", failpointSslContext); + + ClientEncryptionSettings.Builder builder = ClientEncryptionSettings.builder() + .keyVaultMongoClientSettings(getMongoClientSettings()) + .keyVaultNamespace("keyvault.datakeys") + .kmsProviders(kmsProviders) + .kmsProviderSslContextMap(kmsProviderSslContextMap); + if (timeoutMS != null) { + builder.timeout(timeoutMS, TimeUnit.MILLISECONDS); + } + + return getClientEncryption(builder.build()); + } + + private static Map> getKmsProvidersForRetryTest() { + return new HashMap>() {{ + put("aws", new HashMap() {{ + put("accessKeyId", getEnv("AWS_ACCESS_KEY_ID")); + put("secretAccessKey", getEnv("AWS_SECRET_ACCESS_KEY")); + }}); + put("azure", new HashMap() {{ + put("tenantId", getEnv("AZURE_TENANT_ID")); + put("clientId", getEnv("AZURE_CLIENT_ID")); + put("clientSecret", getEnv("AZURE_CLIENT_SECRET")); + put("identityPlatformEndpoint", FAILPOINT_SERVER_ADDRESS); + }}); + put("gcp", new HashMap() {{ + put("email", getEnv("GCP_EMAIL")); + put("privateKey", getEnv("GCP_PRIVATE_KEY")); + put("endpoint", FAILPOINT_SERVER_ADDRESS); + }}); + }}; + } + + private static DataKeyOptions getDataKeyOptions(final String provider) { + BsonDocument masterKey; + switch (provider) { + case "aws": + masterKey = new BsonDocument() + .append("region", new BsonString("foo")) + .append("key", new BsonString("bar")) + .append("endpoint", new BsonString(FAILPOINT_SERVER_ADDRESS)); + break; + case "azure": + masterKey = new BsonDocument() + .append("keyVaultEndpoint", new BsonString(FAILPOINT_SERVER_ADDRESS)) + .append("keyName", new BsonString("foo")); + break; + case "gcp": + masterKey = new BsonDocument() + .append("projectId", new BsonString("foo")) + .append("location", new BsonString("bar")) + .append("keyRing", new BsonString("baz")) + .append("keyName", new BsonString("qux")) + .append("endpoint", new BsonString(FAILPOINT_SERVER_ADDRESS)); + break; + default: + throw new UnsupportedOperationException("Unsupported KMS provider: " + provider); + } + return new DataKeyOptions().masterKey(masterKey); + } + + private static void setFailpoint(final String failpointType, final int count) { + try { + SSLContext sslContext = createFailpointSslContext(); + URL url = new URL(FAILPOINT_URL_BASE + "/set_failpoint/" + failpointType); + HttpsURLConnection connection = (HttpsURLConnection) url.openConnection(); + try { + connection.setConnectTimeout(10_000); + connection.setReadTimeout(10_000); + connection.setSSLSocketFactory(sslContext.getSocketFactory()); + connection.setHostnameVerifier((hostname, session) -> true); + connection.setRequestMethod("POST"); + connection.setDoOutput(true); + connection.setRequestProperty("Content-Type", "application/json"); + + byte[] body = ("{\"count\": " + count + "}").getBytes(StandardCharsets.UTF_8); + connection.setRequestProperty("Content-Length", String.valueOf(body.length)); + + try (OutputStream os = connection.getOutputStream()) { + os.write(body); + } + + int responseCode = connection.getResponseCode(); + assertEquals(200, responseCode, "Failed to set KMS failpoint, HTTP status: " + responseCode); + } finally { + connection.disconnect(); + } + } catch (Exception e) { + throw new RuntimeException("Failed to set KMS failpoint", e); + } + } + + private static SSLContext createFailpointSslContext() { + try { + String caCertPath = System.getProperty("org.mongodb.test.kms.retry.ca.path"); + CertificateFactory cf = CertificateFactory.getInstance("X.509"); + X509Certificate caCert; + try (FileInputStream fis = new FileInputStream(caCertPath)) { + caCert = (X509Certificate) cf.generateCertificate(fis); + } + + KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType()); + trustStore.load(null, null); + trustStore.setCertificateEntry("ca", caCert); + + TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); + tmf.init(trustStore); + + SSLContext sslContext = SSLContext.getInstance("TLS"); + sslContext.init(null, tmf.getTrustManagers(), null); + return sslContext; + } catch (Exception e) { + throw new RuntimeException("Failed to create SSL context for failpoint server", e); + } + } +} diff --git a/driver-sync/src/test/functional/com/mongodb/client/ClientSideEncryptionKmsRetryProseTest.java b/driver-sync/src/test/functional/com/mongodb/client/ClientSideEncryptionKmsRetryProseTest.java new file mode 100644 index 00000000000..7b51bf915e5 --- /dev/null +++ b/driver-sync/src/test/functional/com/mongodb/client/ClientSideEncryptionKmsRetryProseTest.java @@ -0,0 +1,28 @@ +/* + * 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.client; + +import com.mongodb.ClientEncryptionSettings; +import com.mongodb.client.vault.ClientEncryption; +import com.mongodb.client.vault.ClientEncryptions; + +public class ClientSideEncryptionKmsRetryProseTest extends AbstractClientSideEncryptionKmsRetryProseTest { + @Override + public ClientEncryption getClientEncryption(final ClientEncryptionSettings settings) { + return ClientEncryptions.create(settings); + } +} diff --git a/mongodb-crypt/src/main/com/mongodb/internal/crypt/capi/CAPI.java b/mongodb-crypt/src/main/com/mongodb/internal/crypt/capi/CAPI.java index 41cc8ced31b..280f3ea4205 100644 --- a/mongodb-crypt/src/main/com/mongodb/internal/crypt/capi/CAPI.java +++ b/mongodb-crypt/src/main/com/mongodb/internal/crypt/capi/CAPI.java @@ -22,6 +22,7 @@ import com.sun.jna.Native; import com.sun.jna.Pointer; import com.sun.jna.PointerType; +import com.sun.jna.ptr.ByteByReference; import com.sun.jna.ptr.PointerByReference; //CHECKSTYLE:OFF @@ -486,6 +487,21 @@ public interface mongocrypt_random_fn extends Callback { public static native void mongocrypt_setopt_bypass_query_analysis (mongocrypt_t crypt); + /** + * Opt-into handling the MONGOCRYPT_CTX_NEED_KMS state with retry logic. + * + *

If opted in, KMS requests will include retry information accessible via + * {@link #mongocrypt_kms_ctx_usleep}, {@link #mongocrypt_kms_ctx_feed_with_retry}, + * and {@link #mongocrypt_kms_ctx_fail}. + * + * @param crypt The @ref mongocrypt_t object to update + * @param enable Whether to enable KMS retry + * @return A boolean indicating success. If false, an error status is set. + * @since 5.8 + */ + public static native boolean + mongocrypt_setopt_retry_kms(mongocrypt_t crypt, boolean enable); + /** * Set the expiration time for the data encryption key cache. Defaults to 60 seconds if not set. * @@ -1164,6 +1180,47 @@ public interface mongocrypt_random_fn extends Callback { public static native boolean mongocrypt_kms_ctx_feed(mongocrypt_kms_ctx_t kms, mongocrypt_binary_t bytes); + /** + * Get the number of microseconds to sleep before sending the next KMS request. + * + *

Requires {@link #mongocrypt_setopt_retry_kms} to be enabled. + * A return value of 0 indicates no delay is needed. + * + * @param kms The @ref mongocrypt_kms_ctx_t. + * @return The number of microseconds to sleep, or 0. + * @since 5.8 + */ + public static native long + mongocrypt_kms_ctx_usleep(mongocrypt_kms_ctx_t kms); + + /** + * Feed bytes from the HTTP response, with retry support. + * + *

Requires {@link #mongocrypt_setopt_retry_kms} to be enabled. + * + * @param kms The @ref mongocrypt_kms_ctx_t. + * @param bytes The bytes to feed. + * @param should_retry Receives whether the driver should retry the KMS request. + * @return A boolean indicating success. + * @since 5.8 + */ + public static native boolean + mongocrypt_kms_ctx_feed_with_retry(mongocrypt_kms_ctx_t kms, + mongocrypt_binary_t bytes, + ByteByReference should_retry); + + /** + * Signal to libmongocrypt that a network error occurred on this KMS request. + * + *

Requires {@link #mongocrypt_setopt_retry_kms} to be enabled. + * + * @param kms The @ref mongocrypt_kms_ctx_t. + * @return True if the request should be retried, false if retries are exhausted. + * @since 5.8 + */ + public static native boolean + mongocrypt_kms_ctx_fail(mongocrypt_kms_ctx_t kms); + /** * Get the status associated with a @ref mongocrypt_kms_ctx_t object. diff --git a/mongodb-crypt/src/main/com/mongodb/internal/crypt/capi/MongoCryptImpl.java b/mongodb-crypt/src/main/com/mongodb/internal/crypt/capi/MongoCryptImpl.java index 774b9e718cb..5731ca20689 100644 --- a/mongodb-crypt/src/main/com/mongodb/internal/crypt/capi/MongoCryptImpl.java +++ b/mongodb-crypt/src/main/com/mongodb/internal/crypt/capi/MongoCryptImpl.java @@ -73,6 +73,7 @@ import static com.mongodb.internal.crypt.capi.CAPI.mongocrypt_setopt_kms_provider_local; import static com.mongodb.internal.crypt.capi.CAPI.mongocrypt_setopt_kms_providers; import static com.mongodb.internal.crypt.capi.CAPI.mongocrypt_setopt_log_handler; +import static com.mongodb.internal.crypt.capi.CAPI.mongocrypt_setopt_retry_kms; import static com.mongodb.internal.crypt.capi.CAPI.mongocrypt_setopt_schema_map; import static com.mongodb.internal.crypt.capi.CAPI.mongocrypt_setopt_set_crypt_shared_lib_path_override; import static com.mongodb.internal.crypt.capi.CAPI.mongocrypt_setopt_use_need_kms_credentials_state; @@ -198,6 +199,8 @@ class MongoCryptImpl implements MongoCrypt { mongocrypt_setopt_use_need_kms_credentials_state(wrapped); } + configure(() -> mongocrypt_setopt_retry_kms(wrapped, true)); + if (options.getKmsProviderOptions() != null) { withBinaryHolder(options.getKmsProviderOptions(), binary -> configure(() -> mongocrypt_setopt_kms_providers(wrapped, binary))); diff --git a/mongodb-crypt/src/main/com/mongodb/internal/crypt/capi/MongoKeyDecryptor.java b/mongodb-crypt/src/main/com/mongodb/internal/crypt/capi/MongoKeyDecryptor.java index 9b0eae6776f..a8470433fe5 100644 --- a/mongodb-crypt/src/main/com/mongodb/internal/crypt/capi/MongoKeyDecryptor.java +++ b/mongodb-crypt/src/main/com/mongodb/internal/crypt/capi/MongoKeyDecryptor.java @@ -24,6 +24,15 @@ */ public interface MongoKeyDecryptor { + /** + * Initial read size after re-sending a KMS request. Matches libmongocrypt's DEFAULT_MAX_KMS_BYTE_REQUEST + * and is used when {@link #bytesNeeded()} still returns 0 because libmongocrypt's should_retry flag has + * not yet been cleared by {@link #feedAndRetry}. + * + * @since 5.8 + */ + int DEFAULT_KMS_READ_SIZE = 1024; + /** * Gets the name of the KMS provider, e.g. "aws" or "kmip" * @@ -73,4 +82,29 @@ public interface MongoKeyDecryptor { * @param bytes the received bytes */ void feed(ByteBuffer bytes); + + /** + * Gets the number of microseconds to sleep before sending the next KMS request. + * + * @return the number of microseconds to sleep, or 0 if no delay is needed + * @since 5.8 + */ + long sleepMicroseconds(); + + /** + * Feed the received bytes to the decryptor, with retry support. + * + * @param bytes the received bytes + * @return true if the KMS request should be retried + * @since 5.8 + */ + boolean feedAndRetry(ByteBuffer bytes); + + /** + * Signal to libmongocrypt that a network error occurred on this KMS request. + * + * @return true if the request should be retried, false if retries are exhausted + * @since 5.8 + */ + boolean fail(); } diff --git a/mongodb-crypt/src/main/com/mongodb/internal/crypt/capi/MongoKeyDecryptorImpl.java b/mongodb-crypt/src/main/com/mongodb/internal/crypt/capi/MongoKeyDecryptorImpl.java index 1411adffc21..f1ab70c3dbc 100644 --- a/mongodb-crypt/src/main/com/mongodb/internal/crypt/capi/MongoKeyDecryptorImpl.java +++ b/mongodb-crypt/src/main/com/mongodb/internal/crypt/capi/MongoKeyDecryptorImpl.java @@ -22,6 +22,7 @@ import com.mongodb.internal.crypt.capi.CAPI.mongocrypt_kms_ctx_t; import com.mongodb.internal.crypt.capi.CAPI.mongocrypt_status_t; import com.sun.jna.Pointer; +import com.sun.jna.ptr.ByteByReference; import com.sun.jna.ptr.PointerByReference; import java.nio.ByteBuffer; @@ -30,9 +31,12 @@ import static com.mongodb.internal.crypt.capi.CAPI.mongocrypt_binary_new; import static com.mongodb.internal.crypt.capi.CAPI.mongocrypt_kms_ctx_bytes_needed; import static com.mongodb.internal.crypt.capi.CAPI.mongocrypt_kms_ctx_endpoint; +import static com.mongodb.internal.crypt.capi.CAPI.mongocrypt_kms_ctx_fail; import static com.mongodb.internal.crypt.capi.CAPI.mongocrypt_kms_ctx_feed; +import static com.mongodb.internal.crypt.capi.CAPI.mongocrypt_kms_ctx_feed_with_retry; import static com.mongodb.internal.crypt.capi.CAPI.mongocrypt_kms_ctx_get_kms_provider; import static com.mongodb.internal.crypt.capi.CAPI.mongocrypt_kms_ctx_message; +import static com.mongodb.internal.crypt.capi.CAPI.mongocrypt_kms_ctx_usleep; import static com.mongodb.internal.crypt.capi.CAPI.mongocrypt_kms_ctx_status; import static com.mongodb.internal.crypt.capi.CAPI.mongocrypt_status_code; import static com.mongodb.internal.crypt.capi.CAPI.mongocrypt_status_destroy; @@ -42,6 +46,11 @@ import static com.mongodb.internal.crypt.capi.CAPIHelper.toByteBuffer; import static org.bson.assertions.Assertions.notNull; +/** + * Note: Not thread-safe: methods mutate the underlying native {@code mongocrypt_kms_ctx_t} and must be invoked serially. + * Callers perform retries sequentially — the sync driver in a {@code while} loop and the reactive driver via + * {@code Mono.flatMap} — so no external synchronization is required. + */ class MongoKeyDecryptorImpl implements MongoKeyDecryptor { private final mongocrypt_kms_ctx_t wrapped; @@ -96,6 +105,29 @@ public void feed(final ByteBuffer bytes) { } } + @Override + public long sleepMicroseconds() { + return mongocrypt_kms_ctx_usleep(wrapped); + } + + @Override + public boolean feedAndRetry(final ByteBuffer bytes) { + try (BinaryHolder binaryHolder = toBinary(bytes)) { + // Default 0 means "do not retry"; libmongocrypt writes 1 only when the driver should retry. + ByteByReference shouldRetry = new ByteByReference(); + boolean success = mongocrypt_kms_ctx_feed_with_retry(wrapped, binaryHolder.getBinary(), shouldRetry); + if (!success) { + throwExceptionFromStatus(); + } + return shouldRetry.getValue() != 0; + } + } + + @Override + public boolean fail() { + return mongocrypt_kms_ctx_fail(wrapped); + } + private void throwExceptionFromStatus() { mongocrypt_status_t status = mongocrypt_status_new(); mongocrypt_kms_ctx_status(wrapped, status); From f1fd43cc6fcc6cfae35ecc6d1f5b94b8c1be6508 Mon Sep 17 00:00:00 2001 From: Ross Lawley Date: Wed, 3 Jun 2026 12:51:28 +0100 Subject: [PATCH 2/2] Updated CAPI.java javadocs to their 1.18.1 mongocrypt.h equivalents and added a rule to AGENTS.md --- mongodb-crypt/AGENTS.md | 11 + .../com/mongodb/internal/crypt/capi/CAPI.java | 723 +++++++++--------- 2 files changed, 375 insertions(+), 359 deletions(-) diff --git a/mongodb-crypt/AGENTS.md b/mongodb-crypt/AGENTS.md index bf9737febf7..03381f1572b 100644 --- a/mongodb-crypt/AGENTS.md +++ b/mongodb-crypt/AGENTS.md @@ -15,6 +15,17 @@ Client-side field-level encryption (CSFLE) support via JNA bindings to libmongoc review** - `com.mongodb.internal.crypt.capi` — Internal encryption state management +## CAPI.java — JNA Binding Declarations + +`CAPI.java` declares the JNA native method signatures for libmongocrypt. Javadoc on each method +must match the documentation in [mongocrypt.h](https://github.com/mongodb/libmongocrypt/blob/master/src/mongocrypt.h). + +When adding or updating bindings: +- Copy the doc comment from `mongocrypt.h` verbatim +- Replace Doxygen `@ref type_name` with Javadoc `{@link type_name}` (for types) or + `{@link #function_name}` (for functions in the same class) +- Replace `@p param_name` with `{@code param_name}` + ## Build & Test ```bash diff --git a/mongodb-crypt/src/main/com/mongodb/internal/crypt/capi/CAPI.java b/mongodb-crypt/src/main/com/mongodb/internal/crypt/capi/CAPI.java index 280f3ea4205..8e8be93501f 100644 --- a/mongodb-crypt/src/main/com/mongodb/internal/crypt/capi/CAPI.java +++ b/mongodb-crypt/src/main/com/mongodb/internal/crypt/capi/CAPI.java @@ -53,16 +53,16 @@ public String toString() { /** * Indicates success or contains error information. *

- * Functions like @ref mongocrypt_ctx_encrypt_init follow a pattern to expose a + * Functions like {@link #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. + * corresponding (handle)_status function. E.g. {@link #mongocrypt_ctx_status}. */ public static class mongocrypt_status_t extends PointerType { } /** - * Contains all options passed on initialization of a @ref mongocrypt_ctx_t. + * Contains all options passed on initialization of a {@link mongocrypt_ctx_t}. */ public static class mongocrypt_opts_t extends PointerType { } @@ -70,8 +70,19 @@ 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. + * When constructing a {@link mongocrypt_binary_t} it is the responsibility of the + * caller to maintain the lifetime of the viewed data. However, all public + * functions that take a {@link mongocrypt_binary_t} as an argument will make a copy of + * the viewed data. + *

+ * Functions with a {@link mongocrypt_binary_t}* out guarantee the lifetime of the + * viewed data to live as long as the parent object. For example, + * {@link #mongocrypt_ctx_mongo_op} guarantees that the viewed data of + * {@link mongocrypt_binary_t} is valid until the parent ctx is destroyed with + * {@link #mongocrypt_ctx_destroy}. + *

+ * The {@code mongocrypt_binary_t} struct definition is public. + * Consumers may rely on the struct layout. */ public static class mongocrypt_binary_t extends PointerType { // The `mongocrypt_binary_t` struct layout is part of libmongocrypt's ABI: @@ -84,9 +95,11 @@ public static class mongocrypt_binary_t extends PointerType { 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`. @@ -108,7 +121,7 @@ public int len() { * 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 + * handles (e.g. mongocrypt_ctx_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. @@ -153,7 +166,7 @@ public static class mongocrypt_kms_ctx_t extends PointerType { * 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. + * @param len The length of the {@code data} byte array. * @return A new mongocrypt_binary_t. */ public static native mongocrypt_binary_t @@ -161,31 +174,29 @@ public static class mongocrypt_kms_ctx_t extends PointerType { /** - * Get a pointer to the referenced data. + * Get a pointer to the viewed data. * - * @param binary The @ref mongocrypt_binary_t. - * @return A pointer to the referenced data. + * @param binary The {@link mongocrypt_binary_t}. + * @return A pointer to the viewed data. */ public static native Pointer mongocrypt_binary_data(mongocrypt_binary_t binary); /** - * Get the length of the referenced data. + * Get the length of the viewed data. * - * @param binary The @ref mongocrypt_binary_t. - * @return The length of the referenced data. + * @param binary The {@link mongocrypt_binary_t}. + * @return The length of the viewed data. */ public static native int mongocrypt_binary_len(mongocrypt_binary_t binary); /** - * Free the @ref mongocrypt_binary_t. + * Free the {@link mongocrypt_binary_t}. *

- * This does not free the referenced data. Refer to individual function - * documentation to determine the lifetime guarantees of the underlying - * data. + * This does not free the viewed data. * * @param binary The mongocrypt_binary_t destroy. */ @@ -201,8 +212,8 @@ public static class mongocrypt_kms_ctx_t extends PointerType { * 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. + * this as an out-parameter to functions like {@link #mongocrypt_ctx_status}. + * When done, destroy it with {@link #mongocrypt_status_destroy}. * * @return A new status object. */ @@ -218,20 +229,23 @@ public static class mongocrypt_kms_ctx_t extends PointerType { * @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). + * @param message_len Due to historical behavior, pass 1 + the string length + * of {@code message} (which differs from other functions accepting string + * arguments). Alternatively, if message is NULL terminated this may be -1 to + * tell mongocrypt to determine the string's length with strlen. */ public static native void mongocrypt_status_set(mongocrypt_status_t status, - int type, - int code, - cstring message, - int message_len); + 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. + * @return A mongocrypt_status_type_t. */ public static native int @@ -249,11 +263,12 @@ public static class mongocrypt_kms_ctx_t extends PointerType { /** - * Get the error message associated with a status, or an empty string. + * Get the error message associated with a status or NULL. * * @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. + * @param len An optional length of the returned string (excluding the + * trailing NULL byte). May be NULL. + * @return A NULL terminated error message or NULL. */ public static native cstring mongocrypt_status_message(mongocrypt_status_t status, Pointer len); @@ -311,12 +326,12 @@ public interface mongocrypt_random_fn extends Callback { } /** - * Allocate a new @ref mongocrypt_t object. + * Allocate a new {@link mongocrypt_t} object. *

- * Initialize with @ref mongocrypt_init. When done, free with @ref - * mongocrypt_destroy. + * Set options using mongocrypt_setopt_* functions, then initialize with {@link #mongocrypt_init}. + * When done with the {@link mongocrypt_t}, free with {@link #mongocrypt_destroy}. * - * @return A new @ref mongocrypt_t object. + * @return A new {@link mongocrypt_t} object. */ public static native mongocrypt_t mongocrypt_new(); @@ -324,7 +339,7 @@ public interface mongocrypt_random_fn extends Callback { /** * Set a handler to get called on every log message. * - * @param crypt The @ref mongocrypt_t object. + * @param crypt The {@link mongocrypt_t} object. * @param log_fn The log callback. * @param log_ctx A context passed as an argument to the log callback every * invokation. @@ -349,19 +364,18 @@ public interface mongocrypt_random_fn extends Callback { /** * Set a crypto hook for the AES256-CTR operations. * - * @param crypt The @ref mongocrypt_t object. + * @param crypt The {@link mongocrypt_t} object. * @param aes_256_ctr_encrypt The crypto callback function for encrypt - * operation. + * 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 + * 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 {@link #mongocrypt_status} * */ public static native boolean - mongocrypt_setopt_aes_256_ctr (mongocrypt_t crypt, + mongocrypt_setopt_aes_256_ctr(mongocrypt_t crypt, mongocrypt_crypto_fn aes_256_ctr_encrypt, mongocrypt_crypto_fn aes_256_ctr_decrypt, Pointer ctx); @@ -374,12 +388,11 @@ public interface mongocrypt_random_fn extends Callback { *

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 crypt The {@link 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 + * @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 {@link #mongocrypt_status} */ public static native boolean mongocrypt_setopt_crypto_hook_sign_rsaes_pkcs1_v1_5( @@ -388,20 +401,22 @@ public interface mongocrypt_random_fn extends Callback { Pointer sign_ctx); /** - * Set a handler to get called on every log message. + * Configure an AWS KMS provider on the {@link mongocrypt_t} object. * - * @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. + *

This has been superseded by the more flexible: + * {@link #mongocrypt_setopt_kms_providers}. + * + * @param crypt The {@link 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 {@code 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 {@code aws_secret_access_key}. + * 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 {@link #mongocrypt_ctx_status} */ public static native boolean mongocrypt_setopt_kms_provider_aws(mongocrypt_t crypt, @@ -411,11 +426,16 @@ public interface mongocrypt_random_fn extends Callback { int aws_secret_access_key_len); /** - * Configure a local KMS provider on the @ref mongocrypt_t object. + * Configure a local KMS provider on the {@link 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. + *

This has been superseded by the more flexible: + * {@link #mongocrypt_setopt_kms_providers}. + * + * @param crypt The {@link mongocrypt_t} object. + * @param key A 96 byte master key used to encrypt and decrypt key vault + * keys. The viewed data is copied. It is valid to destroy {@code key} with + * {@link #mongocrypt_binary_destroy} immediately after. + * @return A boolean indicating success. If false, an error status is set. Retrieve it with {@link #mongocrypt_ctx_status} */ public static native boolean mongocrypt_setopt_kms_provider_local(mongocrypt_t crypt, @@ -424,10 +444,9 @@ public interface mongocrypt_random_fn extends Callback { /** * Configure KMS providers with a BSON document. * - * @param crypt The @ref mongocrypt_t object. + * @param crypt The {@link 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, @@ -436,41 +455,44 @@ public interface mongocrypt_random_fn extends Callback { /** * Set a local schema map for encryption. * - * @param crypt The @ref mongocrypt_t object. + * @param crypt The {@link 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 + * 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 {@link #mongocrypt_status} */ public static native boolean - mongocrypt_setopt_schema_map (mongocrypt_t crypt, mongocrypt_binary_t schema_map); + mongocrypt_setopt_schema_map(mongocrypt_t crypt, mongocrypt_binary_t schema_map); /** - * Opt-into setting KMS providers before each KMS request. + * Opt-into handling the {@link #MONGOCRYPT_CTX_NEED_KMS_CREDENTIALS} state. * - * 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. + *

If set, before entering the {@link #MONGOCRYPT_CTX_NEED_KMS} state, + * contexts may enter the {@link #MONGOCRYPT_CTX_NEED_KMS_CREDENTIALS} state + * and then wait for credentials to be supplied through + * {@link #mongocrypt_ctx_provide_kms_providers}. * - * @param crypt The @ref mongocrypt_t object to update + *

A context will only enter {@link #MONGOCRYPT_CTX_NEED_KMS_CREDENTIALS} + * if an empty document was set for a KMS provider in + * {@link #mongocrypt_setopt_kms_providers}. + * + * @param crypt The {@link mongocrypt_t} object to update */ public static native void - mongocrypt_setopt_use_need_kms_credentials_state (mongocrypt_t crypt); + mongocrypt_setopt_use_need_kms_credentials_state(mongocrypt_t crypt); /** * Set a local EncryptedFieldConfigMap for encryption. * - * @param crypt The @ref mongocrypt_t object. + * @param crypt The {@link 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 + * supplied by the user. The keys are collection namespaces and values are + * EncryptedFieldConfigMap documents. The viewed data copied. It is valid to + * destroy {@code efc_map} with {@link #mongocrypt_binary_destroy} immediately after. + * @return A boolean indicating success. If false, an error status is set. Retrieve it with {@link #mongocrypt_status} */ public static native boolean - mongocrypt_setopt_encrypted_field_config_map (mongocrypt_t crypt, mongocrypt_binary_t encryptedFieldConfigMap); + mongocrypt_setopt_encrypted_field_config_map(mongocrypt_t crypt, mongocrypt_binary_t encryptedFieldConfigMap); /** * Opt-into skipping query analysis. @@ -478,26 +500,22 @@ public interface mongocrypt_random_fn extends Callback { *

If opted in: *

    *
  • The crypt_shared shared library will not attempt to be loaded.
  • - *
  • A mongocrypt_ctx_t will never enter the MONGOCRYPT_CTX_NEED_MARKINGS state.
  • + *
  • A mongocrypt_ctx_t will never enter the {@link #MONGOCRYPT_CTX_NEED_MONGO_MARKINGS} state.
  • *
* - * @param crypt The @ref mongocrypt_t object to update - * @since 1.5 + * @param crypt The {@link mongocrypt_t} object to update */ public static native void - mongocrypt_setopt_bypass_query_analysis (mongocrypt_t crypt); + mongocrypt_setopt_bypass_query_analysis(mongocrypt_t crypt); /** - * Opt-into handling the MONGOCRYPT_CTX_NEED_KMS state with retry logic. + * Enable or disable KMS retry behavior. * - *

If opted in, KMS requests will include retry information accessible via - * {@link #mongocrypt_kms_ctx_usleep}, {@link #mongocrypt_kms_ctx_feed_with_retry}, - * and {@link #mongocrypt_kms_ctx_fail}. + *

Requires that {@link #mongocrypt_init} has not been called on crypt. * - * @param crypt The @ref mongocrypt_t object to update - * @param enable Whether to enable KMS retry - * @return A boolean indicating success. If false, an error status is set. - * @since 5.8 + * @param crypt The {@link mongocrypt_t} object. + * @param enable A boolean indicating whether to retry operations. + * @return A boolean indicating success. If false, an error status is set. Retrieve it with {@link #mongocrypt_ctx_status} */ public static native boolean mongocrypt_setopt_retry_kms(mongocrypt_t crypt, boolean enable); @@ -505,101 +523,95 @@ public interface mongocrypt_random_fn extends Callback { /** * Set the expiration time for the data encryption key cache. Defaults to 60 seconds if not set. * - * @param crypt The @ref mongocrypt_t object to update + * @param crypt The {@link mongocrypt_t} object to update * @param cache_expiration_ms if 0 the cache never expires * @return A boolean indicating success. If false, an error status is set. - * @since 5.4 */ public static native boolean - mongocrypt_setopt_key_expiration (mongocrypt_t crypt, long cache_expiration_ms); + mongocrypt_setopt_key_expiration(mongocrypt_t crypt, long cache_expiration_ms); /** * Opt-into enabling sending multiple collection info documents. * - * @param crypt The @ref mongocrypt_t object to update + * @param crypt The {@link mongocrypt_t} object to update */ public static native void - mongocrypt_setopt_enable_multiple_collinfo (mongocrypt_t crypt); + mongocrypt_setopt_enable_multiple_collinfo(mongocrypt_t crypt); /** * Set the contention factor used for explicit encryption. * The contention factor is only used for indexed Queryable Encryption. * - * @param ctx The @ref mongocrypt_ctx_t object. + * @param ctx The {@link mongocrypt_ctx_t} object. * @param contention_factor the contention factor - * @return A boolean indicating success. If false, an error status is set. - * Retrieve it with @ref mongocrypt_ctx_status. - * @since 1.5 + * @return A boolean indicating success. If false, an error status is set. Retrieve it with {@link #mongocrypt_ctx_status}. */ public static native boolean - mongocrypt_ctx_setopt_contention_factor (mongocrypt_ctx_t ctx, long contention_factor); + mongocrypt_ctx_setopt_contention_factor(mongocrypt_ctx_t ctx, long contention_factor); /** * Set the index key id to use for Queryable Encryption explicit encryption. + *

+ * If the index key id not set, the key id from {@link #mongocrypt_ctx_setopt_key_id} is used. * - * If the index key id not set, the key id from @ref mongocrypt_ctx_setopt_key_id is used. - * - * @param ctx The @ref mongocrypt_ctx_t object. + * @param ctx The {@link mongocrypt_ctx_t} object. * @param key_id The binary corresponding to the _id (a UUID) of the data key to use from * the key vault collection. Note, the UUID must be encoded with RFC-4122 byte order. - * The viewed data is copied. It is valid to destroy key_id with @ref mongocrypt_binary_destroy immediately after. - * @return A boolean indicating success. If false, an error status is set. - * Retrieve it with @ref mongocrypt_ctx_status - * @since 1.5 + * The viewed data is copied. It is valid to destroy key_id with {@link #mongocrypt_binary_destroy} immediately after. + * @return A boolean indicating success. If false, an error status is set. Retrieve it with {@link #mongocrypt_ctx_status} */ public static native boolean - mongocrypt_ctx_setopt_index_key_id (mongocrypt_ctx_t ctx, mongocrypt_binary_t key_id); + mongocrypt_ctx_setopt_index_key_id(mongocrypt_ctx_t ctx, mongocrypt_binary_t key_id); /** * Append an additional search directory to the search 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 the search path. 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. If - * the path string is literal "$SYSTEM", then libmongocrypt will defer to the - * system's library resolution mechanism to find the crypt_shared library. - * - *

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 + * @param crypt The {@link mongocrypt_t} object to update + * @param path A null-terminated sequence of bytes for the search path. 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. If + * the path string is literal "$SYSTEM", then libmongocrypt will defer to the + * system's library resolution mechanism to find the crypt_shared library. + * + *

If no crypt_shared dynamic library is found in any of the directories + * specified by the search paths loaded here, {@link #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 {@link #mongocrypt_setopt_set_crypt_shared_lib_path_override}, + * then paths appended here will have no effect.

*/ public static native void - mongocrypt_setopt_append_crypt_shared_lib_search_path (mongocrypt_t crypt, cstring path); + 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 + * + * @param crypt The {@link 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 {@link #mongocrypt_init}.

+ *

If a crypt_shared library path override is specified here, then no paths given + * to {@link #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 {@link #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.

*/ public static native void mongocrypt_setopt_set_crypt_shared_lib_path_override(mongocrypt_t crypt, cstring path); @@ -608,80 +620,76 @@ public interface mongocrypt_random_fn extends Callback { * 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 ctx The {@link 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 + * @param len the length + * @return A boolean indicating success. If false, an error status is set. Retrieve it with {@link #mongocrypt_ctx_status} */ public static native boolean - mongocrypt_ctx_setopt_query_type (mongocrypt_ctx_t ctx, cstring query_type, int len); + 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> + * "min": Optional<BSON value>, + * "max": Optional<BSON value>, + * "sparsity": Optional<Int64>, + * "precision": Optional<Int32> + * "trimFactor": Optional<Int32> * } * - * @param ctx The @ref mongocrypt_ctx_t object. + * @param ctx The {@link 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); + mongocrypt_ctx_setopt_algorithm_range(mongocrypt_ctx_t ctx, mongocrypt_binary_t opts); /** * Set options for explicit encryption with the "textPreview" algorithm. "prefix" and "suffix" can both be set. * NOTE: "textPreview" is experimental only and may be removed in a future non-major release. * opts is a BSON document of the form: - * + *

* { - * "caseSensitive": bool, - * "diacriticSensitive": bool, - * "prefix": Optional{ - * "strMaxQueryLength": Int32, - * "strMinQueryLength": Int32, - * }, - * "suffix": Optional{ - * "strMaxQueryLength": Int32, - * "strMinQueryLength": Int32, - * }, - * "substring": Optional{ - * "strMaxLength": Int32, - * "strMaxQueryLength": Int32, - * "strMinQueryLength": Int32, - * }, + * "caseSensitive": bool, + * "diacriticSensitive": bool, + * "prefix": Optional{ + * "strMaxQueryLength": Int32, + * "strMinQueryLength": Int32, + * }, + * "suffix": Optional{ + * "strMaxQueryLength": Int32, + * "strMinQueryLength": Int32, + * }, + * "substring": Optional{ + * "strMaxLength": Int32, + * "strMaxQueryLength": Int32, + * "strMinQueryLength": Int32, + * }, * } * - * @param ctx The @ref mongocrypt_ctx_t object. + * @param ctx The {@link mongocrypt_ctx_t} object. * @param opts BSON. * @return A boolean indicating success. If false, an error status is set. - * @since 5.6 */ public static native boolean mongocrypt_ctx_setopt_algorithm_text(mongocrypt_ctx_t ctx, mongocrypt_binary_t opts); /** - * Initialize new @ref mongocrypt_t object. + * Initialize new {@link mongocrypt_t} object. * - * @param crypt The @ref mongocrypt_t object. + * @param crypt The {@link 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. + * Get the status associated with a {@link mongocrypt_t} object. * - * @param crypt The @ref mongocrypt_t object. + * @param crypt The {@link mongocrypt_t} object. * @param status Receives the status. * @return A boolean indicating success. */ @@ -701,9 +709,9 @@ public interface mongocrypt_random_fn extends Callback { mongocrypt_is_crypto_available(); /** - * Destroy the @ref mongocrypt_t object. + * Destroy the {@link mongocrypt_t} object. * - * @param crypt The @ref mongocrypt_t object to destroy. + * @param crypt The {@link mongocrypt_t} object to destroy. */ public static native void mongocrypt_destroy(mongocrypt_t crypt); @@ -711,45 +719,42 @@ public interface mongocrypt_random_fn extends Callback { /** * 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. - * + * @param crypt The {@link mongocrypt_t} object after a successful call to {@link #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); + mongocrypt_crypt_shared_lib_version_string(mongocrypt_t crypt, Pointer len); /** - * Call in response to the MONGOCRYPT_CTX_NEED_KMS_CREDENTIALS state + * Call in response to the {@link #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 + * as {@link #mongocrypt_setopt_kms_providers}. If no keys are present in the + * BSON input, the KMS provider settings configured for the {@link mongocrypt_t} * at initialization are used. * - * @param ctx The @ref mongocrypt_ctx_t object. + * @param ctx The {@link 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. + * to credentials. + * @return A boolean indicating success. If false, an error status is set. Retrieve it with {@link #mongocrypt_ctx_status}. */ public static native boolean - mongocrypt_ctx_provide_kms_providers (mongocrypt_ctx_t ctx, - mongocrypt_binary_t kms_providers); + 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 ctx The {@link 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); + mongocrypt_ctx_setopt_key_id(mongocrypt_ctx_t ctx, + mongocrypt_binary_t key_id); /** * Set the keyAltName to use for explicit encryption. @@ -758,14 +763,13 @@ public interface mongocrypt_random_fn extends Callback { * *

It is an error to set both this and the key id.

* - * @param ctx The @ref mongocrypt_ctx_t object. + * @param ctx The {@link 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 + * @return A boolean indicating success. If false, an error status is set. Retrieve it with {@link #mongocrypt_ctx_status} */ public static native boolean - mongocrypt_ctx_setopt_key_alt_name (mongocrypt_ctx_t ctx, - mongocrypt_binary_t key_alt_name); + mongocrypt_ctx_setopt_key_alt_name(mongocrypt_ctx_t ctx, + mongocrypt_binary_t key_alt_name); /** * Set the keyMaterial to use for encrypting data. @@ -775,32 +779,30 @@ public interface mongocrypt_random_fn extends Callback { * { "keyMaterial" : (BSON BINARY value) } *

* - * @param ctx The @ref mongocrypt_ctx_t object. + * @param ctx The {@link mongocrypt_ctx_t} object. * @param key_material The data encryption key to use. The viewed data is - * copied. It is valid to destroy @p key_material with @ref - * mongocrypt_binary_destroy immediately after. - * @return A boolean indicating success. If false, an error status is set. - * Retrieve it with @ref mongocrypt_ctx_status + * copied. It is valid to destroy {@code key_material} with {@link #mongocrypt_binary_destroy} immediately after. + * @return A boolean indicating success. If false, an error status is set. Retrieve it with {@link #mongocrypt_ctx_status} */ public static native boolean - mongocrypt_ctx_setopt_key_material (mongocrypt_ctx_t ctx, mongocrypt_binary_t key_material); + mongocrypt_ctx_setopt_key_material(mongocrypt_ctx_t ctx, mongocrypt_binary_t key_material); /** * Set the algorithm used for encryption to either * deterministic or random encryption. This value * should only be set when using explicit encryption. - * + *

* If -1 is passed in for "len", then "algorithm" is * assumed to be a null-terminated string. - * + *

* Valid values for algorithm are: - * "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - * "AEAD_AES_256_CBC_HMAC_SHA_512-Randomized" + * "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" + * "AEAD_AES_256_CBC_HMAC_SHA_512-Random" * - * @param ctx The @ref mongocrypt_ctx_t object. + * @param ctx The {@link mongocrypt_ctx_t} object. * @param algorithm A string specifying the algorithm to - * use for encryption. - * @param len The length of the algorithm string. + * use for encryption. + * @param len The length of the algorithm string. * @return A boolean indicating success. */ public static native boolean @@ -810,12 +812,12 @@ public interface mongocrypt_random_fn extends Callback { /** - * Create a new uninitialized @ref mongocrypt_ctx_t. + * Create a new uninitialized {@link mongocrypt_ctx_t}. *

- * Initialize the context with functions like @ref mongocrypt_ctx_encrypt_init. - * When done, destroy it with @ref mongocrypt_ctx_destroy. + * Initialize the context with functions like {@link #mongocrypt_ctx_encrypt_init}. + * When done, destroy it with {@link #mongocrypt_ctx_destroy}. * - * @param crypt The @ref mongocrypt_t object. + * @param crypt The {@link mongocrypt_t} object. * @return A new context. */ public static native mongocrypt_ctx_t @@ -823,9 +825,9 @@ public interface mongocrypt_random_fn extends Callback { /** - * Get the status associated with a @ref mongocrypt_ctx_t object. + * Get the status associated with a {@link mongocrypt_ctx_t} object. * - * @param ctx The @ref mongocrypt_ctx_t object. + * @param ctx The {@link mongocrypt_ctx_t} object. * @param status Receives the status. * @return A boolean indicating success. */ @@ -837,14 +839,14 @@ public interface mongocrypt_random_fn extends Callback { /** * 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). + * @param ctx The {@link mongocrypt_ctx_t} object. + * @param region The AWS region. + * @param region_len The string length of {@code 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 {@code cmk_len}. Pass -1 to determine the + * string length with strlen (must be NULL terminated). * @return A boolean indicating success. */ public static native boolean @@ -861,12 +863,11 @@ public interface mongocrypt_random_fn extends Callback { * 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 + * @param ctx The {@link mongocrypt_ctx_t} object. + * @param endpoint The endpoint. + * @param endpoint_len The string length of {@code 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 {@link #mongocrypt_ctx_status} */ public static native boolean mongocrypt_ctx_setopt_masterkey_aws_endpoint (mongocrypt_ctx_t ctx, @@ -877,52 +878,47 @@ public interface mongocrypt_random_fn extends Callback { /** * Set the master key to "local" for creating a data key. * - * @param ctx The @ref mongocrypt_ctx_t object. + * @param ctx The {@link mongocrypt_ctx_t} object. * @return A boolean indicating success. */ public static native boolean - mongocrypt_ctx_setopt_masterkey_local (mongocrypt_ctx_t ctx); + 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 ctx The {@link 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); + mongocrypt_binary_t keyDocument); /** * Initialize a context to create a data key. - * - * Set options before using @ref mongocrypt_ctx_setopt_masterkey_aws and + *

+ * Set options before using {@link #mongocrypt_ctx_setopt_masterkey_aws} and * mongocrypt_ctx_setopt_masterkey_local. * - * @param ctx The @ref mongocrypt_ctx_t object. + * @param ctx The {@link 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. + * has been set on the parent {@link mongocrypt_t}. */ public static native boolean - mongocrypt_ctx_datakey_init (mongocrypt_ctx_t ctx); + 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 + * @param ctx The {@link mongocrypt_ctx_t} object. + * @param db The database name. + * @param db_len The byte length of {@code db}. Pass -1 to determine the string length with strlen (must be NULL terminated). + * @param cmd The BSON command to be encrypted. The viewed data is copied. + * It is valid to destroy {@code cmd} with {@link #mongocrypt_binary_destroy} immediately after. + * @return A boolean indicating success. If false, an error status is set. Retrieve it with {@link #mongocrypt_ctx_status} */ public static native boolean mongocrypt_ctx_encrypt_init(mongocrypt_ctx_t ctx, @@ -933,65 +929,67 @@ public interface mongocrypt_random_fn extends Callback { /** * 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. + * @param ctx A {@link mongocrypt_ctx_t}. + * @param msg A {@link 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); + 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 } + * {@code { "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> }}]}
+     *    {$and: [{: {: , {: {:  }}]}
      * 2. An Aggregate Expression of this form:
-     *    {$and: [{<op>: [<fieldpath>, <value1>]}, {<op>: [<fieldpath>, <value2>]}]
+     *    {$and: [{: [, ]}, {: [, ]}]
      *
      * 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 - * + * - {@link #mongocrypt_ctx_setopt_key_id} + * - {@link #mongocrypt_ctx_setopt_key_alt_name} + * - {@link #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 - * + * - {@link #mongocrypt_ctx_setopt_key_id} + * - {@link #mongocrypt_ctx_setopt_index_key_id} + * - {@link #mongocrypt_ctx_setopt_contention_factor} + * - {@link #mongocrypt_ctx_setopt_query_type} + * - {@link #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. + * @param ctx A {@link mongocrypt_ctx_t}. + * @param msg A {@link 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); + mongocrypt_ctx_explicit_encrypt_expression_init(mongocrypt_ctx_t ctx, mongocrypt_binary_t msg); /** * Initialize a context for decryption. @@ -1007,26 +1005,24 @@ public interface mongocrypt_random_fn extends Callback { /** * 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. + * @param ctx A {@link mongocrypt_ctx_t}. + * @param msg A {@link 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); + 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 ctx The {@link 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); + mongocrypt_ctx_rewrap_many_datakey_init(mongocrypt_ctx_t ctx, mongocrypt_binary_t filter); public static final int MONGOCRYPT_CTX_ERROR = 0; @@ -1045,8 +1041,8 @@ public interface mongocrypt_random_fn extends Callback { /** * Get the current state of a context. * - * @param ctx The @ref mongocrypt_ctx_t object. - * @return A @ref mongocrypt_ctx_state_t. + * @param ctx The {@link mongocrypt_ctx_t} object. + * @return A mongocrypt_ctx_state_t. */ public static native int mongocrypt_ctx_state(mongocrypt_ctx_t ctx); @@ -1057,15 +1053,20 @@ public interface mongocrypt_random_fn extends Callback { * 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. + * {@code op_bson} is a BSON document to be used for the operation. + * - For {@link #MONGOCRYPT_CTX_NEED_MONGO_COLLINFO}(_WITH_DB) it is a listCollections filter. + * - For {@link #MONGOCRYPT_CTX_NEED_MONGO_KEYS} it is a find filter. + * - For {@link #MONGOCRYPT_CTX_NEED_MONGO_MARKINGS} it is a command to send to mongocryptd. *

* - * @param ctx The @ref mongocrypt_ctx_t object. - * @param op_bson A BSON document for the MongoDB operation. - * @return A boolean indicating success. + *

The lifetime of {@code op_bson} is tied to the lifetime of {@code ctx}. It is valid + * until {@link #mongocrypt_ctx_destroy} is called. + * + * @param ctx The {@link mongocrypt_ctx_t} object. + * @param op_bson A BSON document for the MongoDB operation. The data + * viewed by {@code op_bson} is guaranteed to be valid until {@code ctx} is destroyed with + * {@link #mongocrypt_ctx_destroy}. + * @return A boolean indicating success. If false, an error status is set. Retrieve it with {@link #mongocrypt_ctx_status} */ public static native boolean mongocrypt_ctx_mongo_op(mongocrypt_ctx_t ctx, mongocrypt_binary_t op_bson); @@ -1077,12 +1078,12 @@ public interface mongocrypt_random_fn extends Callback { * 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 + * - For {@link #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. + * - For {@link #MONGOCRYPT_CTX_NEED_MONGO_KEYS} it is a doc from a find cursor. + * - For {@link #MONGOCRYPT_CTX_NEED_MONGO_MARKINGS} it is a reply from mongocryptd. * - * @param ctx The @ref mongocrypt_ctx_t object. + * @param ctx The {@link mongocrypt_ctx_t} object. * @param reply A BSON document for the MongoDB operation. * @return A boolean indicating success. */ @@ -1093,7 +1094,7 @@ public interface mongocrypt_random_fn extends Callback { /** * Call when done feeding the reply (or replies) back to the context. * - * @param ctx The @ref mongocrypt_ctx_t object. + * @param ctx The {@link mongocrypt_ctx_t} object. * @return A boolean indicating success. */ @@ -1107,35 +1108,36 @@ public interface mongocrypt_random_fn extends Callback { * 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 + * If KMS handles are being handled synchronously, the driver can reuse the same * TLS socket to send HTTP requests and receive responses. + *

+ * The returned KMS handle does not outlive {@code ctx}. * - * @param ctx A @ref mongocrypt_ctx_t. - * @return a new @ref mongocrypt_kms_ctx_t or NULL. + * @param ctx A {@link mongocrypt_ctx_t}. + * @return a new {@link 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); + Pointer len); /** * Get the HTTP request message for a KMS handle. * - * @param kms A @ref mongocrypt_kms_ctx_t. + * @param kms A {@link mongocrypt_kms_ctx_t}. * @param msg The HTTP request to send to KMS. * @return A boolean indicating success. */ @@ -1146,11 +1148,11 @@ public interface mongocrypt_random_fn extends Callback { /** * 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. + * The storage for {@code endpoint} is not owned by the caller, but + * is valid until calling {@link #mongocrypt_ctx_kms_done} on the + * parent {@link mongocrypt_ctx_t}. * - * @param kms A @ref mongocrypt_kms_ctx_t. + * @param kms A {@link mongocrypt_kms_ctx_t}. * @param endpoint The output hostname. * @return A boolean indicating success. */ @@ -1158,9 +1160,9 @@ public interface mongocrypt_random_fn extends Callback { mongocrypt_kms_ctx_endpoint(mongocrypt_kms_ctx_t kms, PointerByReference endpoint); /** - * Indicates how many bytes to feed into @ref mongocrypt_kms_ctx_feed. + * Indicates how many bytes to feed into {@link #mongocrypt_kms_ctx_feed}. * - * @param kms The @ref mongocrypt_kms_ctx_t. + * @param kms The {@link mongocrypt_kms_ctx_t}. * @return The number of requested bytes. */ public static native int @@ -1170,39 +1172,42 @@ public interface mongocrypt_random_fn extends Callback { /** * Feed bytes from the HTTP response. *

- * Feeding more bytes than what has been returned in @ref - * mongocrypt_kms_ctx_bytes_needed is an error. + * Feeding more bytes than what has been returned in + * {@link #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. + * @param kms The {@link mongocrypt_kms_ctx_t}. + * @param bytes The bytes to feed. The viewed data is copied. It is valid to + * destroy bytes with {@link #mongocrypt_binary_destroy} immediately after. + * @return A boolean indicating success. If false, an error status is set. Retrieve it with {@link #mongocrypt_kms_ctx_status} */ public static native boolean mongocrypt_kms_ctx_feed(mongocrypt_kms_ctx_t kms, mongocrypt_binary_t bytes); /** - * Get the number of microseconds to sleep before sending the next KMS request. + * Indicates how long to sleep before sending this request. * *

Requires {@link #mongocrypt_setopt_retry_kms} to be enabled. - * A return value of 0 indicates no delay is needed. * - * @param kms The @ref mongocrypt_kms_ctx_t. - * @return The number of microseconds to sleep, or 0. - * @since 5.8 + * @param kms The {@link mongocrypt_kms_ctx_t}. + * @return How long to sleep in microseconds. */ public static native long mongocrypt_kms_ctx_usleep(mongocrypt_kms_ctx_t kms); /** - * Feed bytes from the HTTP response, with retry support. + * Feed bytes from the KMS response. + * + *

Feeding more bytes than what has been returned in + * {@link #mongocrypt_kms_ctx_bytes_needed} is an error. * *

Requires {@link #mongocrypt_setopt_retry_kms} to be enabled. * - * @param kms The @ref mongocrypt_kms_ctx_t. - * @param bytes The bytes to feed. - * @param should_retry Receives whether the driver should retry the KMS request. - * @return A boolean indicating success. - * @since 5.8 + * @param kms The {@link mongocrypt_kms_ctx_t}. + * @param bytes The bytes to feed. The viewed data is copied. It is valid to + * destroy bytes with {@link #mongocrypt_binary_destroy} immediately after. + * @param should_retry Receives whether the KMS request should be retried. Retry in-place + * without calling {@link #mongocrypt_kms_ctx_fail}. + * @return A boolean indicating success. If false, an error status is set. Retrieve it with {@link #mongocrypt_kms_ctx_status} */ public static native boolean mongocrypt_kms_ctx_feed_with_retry(mongocrypt_kms_ctx_t kms, @@ -1210,35 +1215,35 @@ public interface mongocrypt_random_fn extends Callback { ByteByReference should_retry); /** - * Signal to libmongocrypt that a network error occurred on this KMS request. + * Indicate a network error. Discards all data fed to this KMS context with + * {@link #mongocrypt_kms_ctx_feed}. The {@link mongocrypt_kms_ctx_t} may be reused. * *

Requires {@link #mongocrypt_setopt_retry_kms} to be enabled. * - * @param kms The @ref mongocrypt_kms_ctx_t. - * @return True if the request should be retried, false if retries are exhausted. - * @since 5.8 + * @param kms The {@link mongocrypt_kms_ctx_t}. + * @return A boolean indicating whether the failed request may be retried. */ public static native boolean mongocrypt_kms_ctx_fail(mongocrypt_kms_ctx_t kms); /** - * Get the status associated with a @ref mongocrypt_kms_ctx_t object. + * Get the status associated with a {@link mongocrypt_kms_ctx_t} object. * - * @param kms The @ref mongocrypt_kms_ctx_t object. + * @param kms The {@link 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); + mongocrypt_status_t status); /** * Call when done handling all KMS contexts. * - * @param ctx The @ref mongocrypt_ctx_t object. - * @return A boolean indicating success. + * @param ctx The {@link mongocrypt_ctx_t} object. + * @return A boolean indicating success. If false, an error status is set. Retrieve it with {@link #mongocrypt_ctx_status} */ public static native boolean mongocrypt_ctx_kms_done(mongocrypt_ctx_t ctx); @@ -1247,7 +1252,7 @@ public interface mongocrypt_random_fn extends Callback { /** * Perform the final encryption or decryption. * - * @param ctx A @ref mongocrypt_ctx_t. + * @param ctx A {@link mongocrypt_ctx_t}. * @param out The final BSON to send to the server. * @return a boolean indicating success. */ @@ -1256,9 +1261,9 @@ public interface mongocrypt_random_fn extends Callback { /** - * Destroy and free all memory associated with a @ref mongocrypt_ctx_t. + * Destroy and free all memory associated with a {@link mongocrypt_ctx_t}. * - * @param ctx A @ref mongocrypt_ctx_t. + * @param ctx A {@link mongocrypt_ctx_t}. */ public static native void mongocrypt_ctx_destroy(mongocrypt_ctx_t ctx);