/*
 * Copyright 2021-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.
 */

#include <mongocrypt.h>

#include "mongocrypt-buffer-private.h"
#include "mongocrypt-key-private.h"
#include "mongocrypt-kms-ctx-private.h"
#include "mongocrypt-private.h"
#include "test-mongocrypt-assert.h"
#include "test-mongocrypt-util.h"
#include "test-mongocrypt.h"

/*
<RequestMessage tag="0x420078" type="Structure">
 <RequestHeader tag="0x420077" type="Structure">
  <ProtocolVersion tag="0x420069" type="Structure">
   <ProtocolVersionMajor tag="0x42006a" type="Integer" value="1"/>
   <ProtocolVersionMinor tag="0x42006b" type="Integer" value="0"/>
  </ProtocolVersion>
  <BatchCount tag="0x42000d" type="Integer" value="1"/>
 </RequestHeader>
 <BatchItem tag="0x42000f" type="Structure">
  <Operation tag="0x42005c" type="Enumeration" value="3"/>
  <RequestPayload tag="0x420079" type="Structure">
   <ObjectType tag="0x420057" type="Enumeration" value="7"/>
   <TemplateAttribute tag="0x420091" type="Structure">
    <Attribute tag="0x420008" type="Structure">
     <AttributeName tag="0x42000a" type="TextString" value="Cryptographic Usage
Mask"/> <AttributeValue tag="0x42000b" type="Integer" value="0"/>
    </Attribute>
   </TemplateAttribute>
   <SecretData tag="0x420085" type="Structure">
    <SecretDataType tag="0x420086" type="Enumeration" value="2"/>
    <KeyBlock tag="0x420040" type="Structure">
     <KeyFormatType tag="0x420042" type="Enumeration" value="2"/>
     <KeyValue tag="0x420045" type="Structure">
      <KeyMaterial tag="0x420043" type="ByteString"
value="000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"/>
     </KeyValue>
    </KeyBlock>
   </SecretData>
  </RequestPayload>
 </BatchItem>
</RequestMessage>
*/
static const uint8_t REGISTER_REQUEST[] = {
    0x42, 0x00, 0x78, 0x01, 0x00, 0x00, 0x01, 0x50, 0x42, 0x00, 0x77, 0x01, 0x00, 0x00, 0x00, 0x38, 0x42, 0x00, 0x69,
    0x01, 0x00, 0x00, 0x00, 0x20, 0x42, 0x00, 0x6a, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
    0x00, 0x00, 0x42, 0x00, 0x6b, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42,
    0x00, 0x0d, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00, 0x0f, 0x01,
    0x00, 0x00, 0x01, 0x08, 0x42, 0x00, 0x5c, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
    0x00, 0x42, 0x00, 0x79, 0x01, 0x00, 0x00, 0x00, 0xf0, 0x42, 0x00, 0x57, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00,
    0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00, 0x91, 0x01, 0x00, 0x00, 0x00, 0x38, 0x42, 0x00, 0x08, 0x01, 0x00,
    0x00, 0x00, 0x30, 0x42, 0x00, 0x0a, 0x07, 0x00, 0x00, 0x00, 0x18, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x67, 0x72,
    0x61, 0x70, 0x68, 0x69, 0x63, 0x20, 0x55, 0x73, 0x61, 0x67, 0x65, 0x20, 0x4d, 0x61, 0x73, 0x6b, 0x42, 0x00, 0x0b,
    0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00, 0x85, 0x01, 0x00, 0x00,
    0x00, 0x98, 0x42, 0x00, 0x86, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x42,
    0x00, 0x40, 0x01, 0x00, 0x00, 0x00, 0x80, 0x42, 0x00, 0x42, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02,
    0x00, 0x00, 0x00, 0x00, 0x42, 0x00, 0x45, 0x01, 0x00, 0x00, 0x00, 0x68, 0x42, 0x00, 0x43, 0x08, 0x00, 0x00, 0x00,
    0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00};

/*
<ResponseMessage tag="0x42007b" type="Structure">
 <ResponseHeader tag="0x42007a" type="Structure">
  <ProtocolVersion tag="0x420069" type="Structure">
   <ProtocolVersionMajor tag="0x42006a" type="Integer" value="1"/>
   <ProtocolVersionMinor tag="0x42006b" type="Integer" value="0"/>
  </ProtocolVersion>
  <TimeStamp tag="0x420092" type="DateTime" value="2021-10-12T14:09:25-0500"/>
  <BatchCount tag="0x42000d" type="Integer" value="1"/>
 </ResponseHeader>
 <BatchItem tag="0x42000f" type="Structure">
  <Operation tag="0x42005c" type="Enumeration" value="3"/>
  <ResultStatus tag="0x42007f" type="Enumeration" value="0"/>
  <ResponsePayload tag="0x42007c" type="Structure">
   <UniqueIdentifier tag="0x420094" type="TextString" value="39"/>
  </ResponsePayload>
 </BatchItem>
</ResponseMessage>
*/
static const uint8_t SUCCESS_REGISTER_RESPONSE[] = {
    0x42, 0x00, 0x7b, 0x01, 0x00, 0x00, 0x00, 0x90, 0x42, 0x00, 0x7a, 0x01, 0x00, 0x00, 0x00, 0x48, 0x42, 0x00, 0x69,
    0x01, 0x00, 0x00, 0x00, 0x20, 0x42, 0x00, 0x6a, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
    0x00, 0x00, 0x42, 0x00, 0x6b, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42,
    0x00, 0x92, 0x09, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x61, 0x65, 0x97, 0x15, 0x42, 0x00, 0x0d, 0x02,
    0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00, 0x0f, 0x01, 0x00, 0x00, 0x00,
    0x38, 0x42, 0x00, 0x5c, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00,
    0x7f, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00, 0x7c, 0x01, 0x00,
    0x00, 0x00, 0x10, 0x42, 0x00, 0x94, 0x07, 0x00, 0x00, 0x00, 0x02, 0x33, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};

static const char *const SUCCESS_REGISTER_RESPONSE_UNIQUE_IDENTIFIER = "39";

static void _test_mongocrypt_kms_ctx_kmip_register(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt;
    mongocrypt_kms_ctx_t kms_ctx = {0};
    bool ok;
    uint8_t secretdata[KMS_KMIP_REQUEST_SECRETDATA_LENGTH] = {0};
    mongocrypt_binary_t *bytes;
    _mongocrypt_buffer_t result;
    mongocrypt_status_t *status;
    _mongocrypt_endpoint_t *endpoint;

    status = mongocrypt_status_new();
    endpoint = _mongocrypt_endpoint_new("example.com", -1, NULL /* opts */, status);
    ASSERT_OK_STATUS(endpoint != NULL, status);

    crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
    ok = _mongocrypt_kms_ctx_init_kmip_register(&kms_ctx,
                                                endpoint,
                                                secretdata,
                                                KMS_KMIP_REQUEST_SECRETDATA_LENGTH,
                                                "kmip",
                                                &crypt->log);
    ASSERT_OK_STATUS(ok, kms_ctx.status);

    bytes = mongocrypt_binary_new();
    ok = mongocrypt_kms_ctx_message(&kms_ctx, bytes);
    ASSERT_OK_STATUS(ok, kms_ctx.status);

    ASSERT_CMPBYTES(REGISTER_REQUEST,
                    sizeof(REGISTER_REQUEST),
                    mongocrypt_binary_data(bytes),
                    mongocrypt_binary_len(bytes));
    ASSERT_OK(kms_ctx_feed_all(&kms_ctx, SUCCESS_REGISTER_RESPONSE, sizeof(SUCCESS_REGISTER_RESPONSE)), &kms_ctx);

    ok = _mongocrypt_kms_ctx_result(&kms_ctx, &result);
    ASSERT_OK_STATUS(ok, kms_ctx.status);
    ASSERT_STREQUAL((char *)result.data, SUCCESS_REGISTER_RESPONSE_UNIQUE_IDENTIFIER);

    mongocrypt_binary_destroy(bytes);
    _mongocrypt_endpoint_destroy(endpoint);
    mongocrypt_status_destroy(status);
    _mongocrypt_kms_ctx_cleanup(&kms_ctx);
    mongocrypt_destroy(crypt);
}

/*
<RequestMessage tag="0x420078" type="Structure">
 <RequestHeader tag="0x420077" type="Structure">
  <ProtocolVersion tag="0x420069" type="Structure">
   <ProtocolVersionMajor tag="0x42006a" type="Integer" value="1"/>
   <ProtocolVersionMinor tag="0x42006b" type="Integer" value="0"/>
  </ProtocolVersion>
  <BatchCount tag="0x42000d" type="Integer" value="1"/>
 </RequestHeader>
 <BatchItem tag="0x42000f" type="Structure">
  <Operation tag="0x42005c" type="Enumeration" value="18"/>
  <RequestPayload tag="0x420079" type="Structure">
   <UniqueIdentifier tag="0x420094" type="TextString" value="39"/>
  </RequestPayload>
 </BatchItem>
</RequestMessage>
*/
static const uint8_t ACTIVATE_REQUEST[] = {
    0x42, 0x00, 0x78, 0x01, 0x00, 0x00, 0x00, 0x70, 0x42, 0x00, 0x77, 0x01, 0x00, 0x00, 0x00, 0x38, 0x42, 0x00,
    0x69, 0x01, 0x00, 0x00, 0x00, 0x20, 0x42, 0x00, 0x6a, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01,
    0x00, 0x00, 0x00, 0x00, 0x42, 0x00, 0x6b, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x42, 0x00, 0x0d, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
    0x42, 0x00, 0x0f, 0x01, 0x00, 0x00, 0x00, 0x28, 0x42, 0x00, 0x5c, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00,
    0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00, 0x79, 0x01, 0x00, 0x00, 0x00, 0x10, 0x42, 0x00, 0x94, 0x07,
    0x00, 0x00, 0x00, 0x02, 0x33, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
/*
<ResponseMessage tag="0x42007b" type="Structure">
 <ResponseHeader tag="0x42007a" type="Structure">
  <ProtocolVersion tag="0x420069" type="Structure">
   <ProtocolVersionMajor tag="0x42006a" type="Integer" value="1"/>
   <ProtocolVersionMinor tag="0x42006b" type="Integer" value="0"/>
  </ProtocolVersion>
  <TimeStamp tag="0x420092" type="DateTime" value="2021-10-12T14:09:25-0500"/>
  <BatchCount tag="0x42000d" type="Integer" value="1"/>
 </ResponseHeader>
 <BatchItem tag="0x42000f" type="Structure">
  <Operation tag="0x42005c" type="Enumeration" value="3"/>
  <ResultStatus tag="0x42007f" type="Enumeration" value="0"/>
  <ResponsePayload tag="0x42007c" type="Structure">
   <UniqueIdentifier tag="0x420094" type="TextString" value="39"/>
  </ResponsePayload>
 </BatchItem>
</ResponseMessage>
*/
static const uint8_t SUCCESS_ACTIVATE_RESPONSE[] = {
    0x42, 0x00, 0x7b, 0x01, 0x00, 0x00, 0x00, 0x90, 0x42, 0x00, 0x7a, 0x01, 0x00, 0x00, 0x00, 0x48, 0x42, 0x00, 0x69,
    0x01, 0x00, 0x00, 0x00, 0x20, 0x42, 0x00, 0x6a, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
    0x00, 0x00, 0x42, 0x00, 0x6b, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42,
    0x00, 0x92, 0x09, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x61, 0x65, 0x97, 0x15, 0x42, 0x00, 0x0d, 0x02,
    0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00, 0x0f, 0x01, 0x00, 0x00, 0x00,
    0x38, 0x42, 0x00, 0x5c, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00,
    0x7f, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00, 0x7c, 0x01, 0x00,
    0x00, 0x00, 0x10, 0x42, 0x00, 0x94, 0x07, 0x00, 0x00, 0x00, 0x02, 0x33, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};

static const char *const SUCCESS_ACTIVATE_RESPONSE_UNIQUE_IDENTIFIER = "39";

static void _test_mongocrypt_kms_ctx_kmip_activate(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt;
    mongocrypt_kms_ctx_t kms_ctx = {0};
    bool ok;
    mongocrypt_binary_t *bytes;
    _mongocrypt_buffer_t result;
    mongocrypt_status_t *status;
    _mongocrypt_endpoint_t *endpoint;

    status = mongocrypt_status_new();
    endpoint = _mongocrypt_endpoint_new("example.com", -1, NULL /* opts */, status);
    ASSERT_OK_STATUS(endpoint != NULL, status);

    crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
    ok = _mongocrypt_kms_ctx_init_kmip_activate(&kms_ctx,
                                                endpoint,
                                                (char *)SUCCESS_ACTIVATE_RESPONSE_UNIQUE_IDENTIFIER,
                                                "kmip",
                                                &crypt->log);
    ASSERT_OK_STATUS(ok, kms_ctx.status);

    bytes = mongocrypt_binary_new();
    ok = mongocrypt_kms_ctx_message(&kms_ctx, bytes);
    ASSERT_OK_STATUS(ok, kms_ctx.status);

    ASSERT_CMPBYTES(ACTIVATE_REQUEST,
                    sizeof(ACTIVATE_REQUEST),
                    mongocrypt_binary_data(bytes),
                    mongocrypt_binary_len(bytes));
    ASSERT_OK(kms_ctx_feed_all(&kms_ctx, SUCCESS_ACTIVATE_RESPONSE, sizeof(SUCCESS_ACTIVATE_RESPONSE)), &kms_ctx);

    ok = _mongocrypt_kms_ctx_result(&kms_ctx, &result);
    ASSERT_OK_STATUS(ok, kms_ctx.status);
    ASSERT_STREQUAL((char *)result.data, SUCCESS_ACTIVATE_RESPONSE_UNIQUE_IDENTIFIER);

    mongocrypt_binary_destroy(bytes);
    _mongocrypt_endpoint_destroy(endpoint);
    mongocrypt_status_destroy(status);
    _mongocrypt_kms_ctx_cleanup(&kms_ctx);
    mongocrypt_destroy(crypt);
}

/*
<RequestMessage tag="0x420078" type="Structure">
 <RequestHeader tag="0x420077" type="Structure">
  <ProtocolVersion tag="0x420069" type="Structure">
   <ProtocolVersionMajor tag="0x42006a" type="Integer" value="1"/>
   <ProtocolVersionMinor tag="0x42006b" type="Integer" value="0"/>
  </ProtocolVersion>
  <BatchCount tag="0x42000d" type="Integer" value="1"/>
 </RequestHeader>
 <BatchItem tag="0x42000f" type="Structure">
  <Operation tag="0x42005c" type="Enumeration" value="10"/>
  <RequestPayload tag="0x420079" type="Structure">
   <UniqueIdentifier tag="0x420094" type="TextString" value="39"/>
  </RequestPayload>
 </BatchItem>
</RequestMessage>
*/
static const uint8_t GET_REQUEST[] = {
    0x42, 0x00, 0x78, 0x01, 0x00, 0x00, 0x00, 0x70, 0x42, 0x00, 0x77, 0x01, 0x00, 0x00, 0x00, 0x38, 0x42, 0x00,
    0x69, 0x01, 0x00, 0x00, 0x00, 0x20, 0x42, 0x00, 0x6a, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01,
    0x00, 0x00, 0x00, 0x00, 0x42, 0x00, 0x6b, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x42, 0x00, 0x0d, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
    0x42, 0x00, 0x0f, 0x01, 0x00, 0x00, 0x00, 0x28, 0x42, 0x00, 0x5c, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00,
    0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00, 0x79, 0x01, 0x00, 0x00, 0x00, 0x10, 0x42, 0x00, 0x94, 0x07,
    0x00, 0x00, 0x00, 0x02, 0x33, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
static const char *const GET_REQUEST_UNIQUE_IDENTIFIER = "39";

/*
<ResponseMessage tag="0x42007b" type="Structure">
 <ResponseHeader tag="0x42007a" type="Structure">
  <ProtocolVersion tag="0x420069" type="Structure">
   <ProtocolVersionMajor tag="0x42006a" type="Integer" value="1"/>
   <ProtocolVersionMinor tag="0x42006b" type="Integer" value="4"/>
  </ProtocolVersion>
  <TimeStamp tag="0x420092" type="DateTime" value="2021-10-12T14:09:25-0500"/>
  <BatchCount tag="0x42000d" type="Integer" value="1"/>
 </ResponseHeader>
 <BatchItem tag="0x42000f" type="Structure">
  <Operation tag="0x42005c" type="Enumeration" value="10"/>
  <ResultStatus tag="0x42007f" type="Enumeration" value="0"/>
  <ResponsePayload tag="0x42007c" type="Structure">
   <ObjectType tag="0x420057" type="Enumeration" value="7"/>
   <UniqueIdentifier tag="0x420094" type="TextString" value="39"/>
   <SecretData tag="0x420085" type="Structure">
    <SecretDataType tag="0x420086" type="Enumeration" value="1"/>
    <KeyBlock tag="0x420040" type="Structure">
     <KeyFormatType tag="0x420042" type="Enumeration" value="2"/>
     <KeyValue tag="0x420045" type="Structure">
      <KeyMaterial tag="0x420043" type="ByteString"
value="ffa8cc79e8c3763b0121fcd06bb3488c8bf42c0774604640279b16b264194030eeb08396241defcc4d32d16ea831ad777138f08e2f985664c004c2485d6f4991eb3d9ec32802537836a9066b4e10aeb56a5ccf6aa46901e625e3400c7811d2ec"/>
     </KeyValue>
    </KeyBlock>
   </SecretData>
  </ResponsePayload>
 </BatchItem>
</ResponseMessage>
*/
static const uint8_t SUCCESS_GET_RESPONSE[] = {
    0x42, 0x00, 0x7b, 0x01, 0x00, 0x00, 0x01, 0x40, 0x42, 0x00, 0x7a, 0x01, 0x00, 0x00, 0x00, 0x48, 0x42, 0x00, 0x69,
    0x01, 0x00, 0x00, 0x00, 0x20, 0x42, 0x00, 0x6a, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
    0x00, 0x00, 0x42, 0x00, 0x6b, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x42,
    0x00, 0x92, 0x09, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x61, 0x65, 0x97, 0x15, 0x42, 0x00, 0x0d, 0x02,
    0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00, 0x0f, 0x01, 0x00, 0x00, 0x00,
    0xe8, 0x42, 0x00, 0x5c, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00,
    0x7f, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00, 0x7c, 0x01, 0x00,
    0x00, 0x00, 0xc0, 0x42, 0x00, 0x57, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00,
    0x42, 0x00, 0x94, 0x07, 0x00, 0x00, 0x00, 0x02, 0x33, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00, 0x85,
    0x01, 0x00, 0x00, 0x00, 0x98, 0x42, 0x00, 0x86, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
    0x00, 0x00, 0x42, 0x00, 0x40, 0x01, 0x00, 0x00, 0x00, 0x80, 0x42, 0x00, 0x42, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00,
    0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00, 0x45, 0x01, 0x00, 0x00, 0x00, 0x68, 0x42, 0x00, 0x43, 0x08,
    0x00, 0x00, 0x00, 0x60, 0xff, 0xa8, 0xcc, 0x79, 0xe8, 0xc3, 0x76, 0x3b, 0x01, 0x21, 0xfc, 0xd0, 0x6b, 0xb3, 0x48,
    0x8c, 0x8b, 0xf4, 0x2c, 0x07, 0x74, 0x60, 0x46, 0x40, 0x27, 0x9b, 0x16, 0xb2, 0x64, 0x19, 0x40, 0x30, 0xee, 0xb0,
    0x83, 0x96, 0x24, 0x1d, 0xef, 0xcc, 0x4d, 0x32, 0xd1, 0x6e, 0xa8, 0x31, 0xad, 0x77, 0x71, 0x38, 0xf0, 0x8e, 0x2f,
    0x98, 0x56, 0x64, 0xc0, 0x04, 0xc2, 0x48, 0x5d, 0x6f, 0x49, 0x91, 0xeb, 0x3d, 0x9e, 0xc3, 0x28, 0x02, 0x53, 0x78,
    0x36, 0xa9, 0x06, 0x6b, 0x4e, 0x10, 0xae, 0xb5, 0x6a, 0x5c, 0xcf, 0x6a, 0xa4, 0x69, 0x01, 0xe6, 0x25, 0xe3, 0x40,
    0x0c, 0x78, 0x11, 0xd2, 0xec};

static const uint8_t SUCCESS_GET_RESPONSE_SECRETDATA[] = {
    0xff, 0xa8, 0xcc, 0x79, 0xe8, 0xc3, 0x76, 0x3b, 0x01, 0x21, 0xfc, 0xd0, 0x6b, 0xb3, 0x48, 0x8c,
    0x8b, 0xf4, 0x2c, 0x07, 0x74, 0x60, 0x46, 0x40, 0x27, 0x9b, 0x16, 0xb2, 0x64, 0x19, 0x40, 0x30,
    0xee, 0xb0, 0x83, 0x96, 0x24, 0x1d, 0xef, 0xcc, 0x4d, 0x32, 0xd1, 0x6e, 0xa8, 0x31, 0xad, 0x77,
    0x71, 0x38, 0xf0, 0x8e, 0x2f, 0x98, 0x56, 0x64, 0xc0, 0x04, 0xc2, 0x48, 0x5d, 0x6f, 0x49, 0x91,
    0xeb, 0x3d, 0x9e, 0xc3, 0x28, 0x02, 0x53, 0x78, 0x36, 0xa9, 0x06, 0x6b, 0x4e, 0x10, 0xae, 0xb5,
    0x6a, 0x5c, 0xcf, 0x6a, 0xa4, 0x69, 0x01, 0xe6, 0x25, 0xe3, 0x40, 0x0c, 0x78, 0x11, 0xd2, 0xec};

static void _test_mongocrypt_kms_ctx_kmip_get(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt;
    mongocrypt_kms_ctx_t kms_ctx = {0};
    bool ok;
    mongocrypt_binary_t *bytes;
    _mongocrypt_buffer_t result;
    mongocrypt_status_t *status;
    _mongocrypt_endpoint_t *endpoint;

    status = mongocrypt_status_new();
    endpoint = _mongocrypt_endpoint_new("example.com", -1, NULL /* opts */, status);
    ASSERT_OK_STATUS(endpoint != NULL, status);

    crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
    ok = _mongocrypt_kms_ctx_init_kmip_get(&kms_ctx,
                                           endpoint,
                                           (char *)GET_REQUEST_UNIQUE_IDENTIFIER,
                                           "kmip",
                                           &crypt->log);
    ASSERT_OK_STATUS(ok, kms_ctx.status);

    bytes = mongocrypt_binary_new();
    ok = mongocrypt_kms_ctx_message(&kms_ctx, bytes);
    ASSERT_OK_STATUS(ok, kms_ctx.status);

    ASSERT_CMPBYTES(GET_REQUEST, sizeof(GET_REQUEST), mongocrypt_binary_data(bytes), mongocrypt_binary_len(bytes));
    ASSERT_OK(kms_ctx_feed_all(&kms_ctx, SUCCESS_GET_RESPONSE, sizeof(SUCCESS_GET_RESPONSE)), &kms_ctx);

    ok = _mongocrypt_kms_ctx_result(&kms_ctx, &result);
    ASSERT_OK_STATUS(ok, kms_ctx.status);
    ASSERT_CMPBYTES(result.data, result.len, SUCCESS_GET_RESPONSE_SECRETDATA, sizeof(SUCCESS_GET_RESPONSE_SECRETDATA));

    mongocrypt_binary_destroy(bytes);
    _mongocrypt_endpoint_destroy(endpoint);
    mongocrypt_status_destroy(status);
    _mongocrypt_kms_ctx_cleanup(&kms_ctx);
    mongocrypt_destroy(crypt);
}

/*
<RequestMessage tag="0x420078" type="Structure">
 <RequestHeader tag="0x420077" type="Structure">
  <ProtocolVersion tag="0x420069" type="Structure">
   <ProtocolVersionMajor tag="0x42006a" type="Integer" value="1"/>
   <ProtocolVersionMinor tag="0x42006b" type="Integer" value="2"/>
  </ProtocolVersion>
  <BatchCount tag="0x42000d" type="Integer" value="1"/>
 </RequestHeader>
 <BatchItem tag="0x42000f" type="Structure">
  <Operation tag="0x42005c" type="Enumeration" value="1"/><!-- Create -->
  <RequestPayload tag="0x420079" type="Structure">
   <ObjectType tag="0x420057" type="Enumeration" value="2"/>
   <TemplateAttribute tag="0x420091" type="Structure">
    <Attribute tag="0x420008" type="Structure">
     <AttributeName tag="0x42000a" type="TextString" value="Cryptographic Algorithm"/>
     <AttributeValue tag="0x42000b" type="Enumeration" value="3"/>
    </Attribute>
    <Attribute tag="0x420008" type="Structure">
     <AttributeName tag="0x42000a" type="TextString" value="Cryptographic Length"/>
     <AttributeValue tag="0x42000b" type="Integer" value="256"/>
    </Attribute>
    <Attribute tag="0x420008" type="Structure">
     <AttributeName tag="0x42000a" type="TextString" value="Cryptographic Usage Mask"/>
     <AttributeValue tag="0x42000b" type="Integer" value="12"/>
    </Attribute>
   </TemplateAttribute>
  </RequestPayload>
 </BatchItem>
</RequestMessage>
*/
static const uint8_t CREATE_REQUEST[] = {
    0x42, 0x00, 0x78, 0x01, 0x00, 0x00, 0x01, 0x20, 0x42, 0x00, 0x77, 0x01, 0x00, 0x00, 0x00, 0x38, 0x42, 0x00, 0x69,
    0x01, 0x00, 0x00, 0x00, 0x20, 0x42, 0x00, 0x6a, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
    0x00, 0x00, 0x42, 0x00, 0x6b, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x42,
    0x00, 0x0d, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00, 0x0f, 0x01,
    0x00, 0x00, 0x00, 0xd8, 0x42, 0x00, 0x5c, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
    0x00, 0x42, 0x00, 0x79, 0x01, 0x00, 0x00, 0x00, 0xc0, 0x42, 0x00, 0x57, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00,
    0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00, 0x91, 0x01, 0x00, 0x00, 0x00, 0xa8, 0x42, 0x00, 0x08, 0x01, 0x00,
    0x00, 0x00, 0x30, 0x42, 0x00, 0x0a, 0x07, 0x00, 0x00, 0x00, 0x17, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x67, 0x72,
    0x61, 0x70, 0x68, 0x69, 0x63, 0x20, 0x41, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x00, 0x42, 0x00, 0x0b,
    0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00, 0x08, 0x01, 0x00, 0x00,
    0x00, 0x30, 0x42, 0x00, 0x0a, 0x07, 0x00, 0x00, 0x00, 0x14, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x67, 0x72, 0x61,
    0x70, 0x68, 0x69, 0x63, 0x20, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00, 0x0b, 0x02,
    0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00, 0x08, 0x01, 0x00, 0x00, 0x00,
    0x30, 0x42, 0x00, 0x0a, 0x07, 0x00, 0x00, 0x00, 0x18, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x67, 0x72, 0x61, 0x70,
    0x68, 0x69, 0x63, 0x20, 0x55, 0x73, 0x61, 0x67, 0x65, 0x20, 0x4d, 0x61, 0x73, 0x6b, 0x42, 0x00, 0x0b, 0x02, 0x00,
    0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00};

/*
<ResponseMessage tag="0x42007b" type="Structure">
 <ResponseHeader tag="0x42007a" type="Structure">
  <ProtocolVersion tag="0x420069" type="Structure">
   <ProtocolVersionMajor tag="0x42006a" type="Integer" value="1"/>
   <ProtocolVersionMinor tag="0x42006b" type="Integer" value="2"/>
  </ProtocolVersion>
  <TimeStamp tag="0x420092" type="DateTime" value="2024-01-04T19:10:18+0000"/>
  <BatchCount tag="0x42000d" type="Integer" value="1"/>
 </ResponseHeader>
 <BatchItem tag="0x42000f" type="Structure">
  <Operation tag="0x42005c" type="Enumeration" value="1"/><!-- Create -->
  <ResultStatus tag="0x42007f" type="Enumeration" value="0"/>
  <ResponsePayload tag="0x42007c" type="Structure">
   <ObjectType tag="0x420057" type="Enumeration" value="2"/>
   <UniqueIdentifier tag="0x420094" type="TextString" value="108"/>
  </ResponsePayload>
 </BatchItem>
</ResponseMessage>
*/
static const uint8_t SUCCESS_CREATE_RESPONSE[] = {
    0x42, 0x00, 0x7b, 0x01, 0x00, 0x00, 0x00, 0xa0, 0x42, 0x00, 0x7a, 0x01, 0x00, 0x00, 0x00, 0x48, 0x42, 0x00, 0x69,
    0x01, 0x00, 0x00, 0x00, 0x20, 0x42, 0x00, 0x6a, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
    0x00, 0x00, 0x42, 0x00, 0x6b, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x42,
    0x00, 0x92, 0x09, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x65, 0x97, 0x02, 0x9a, 0x42, 0x00, 0x0d, 0x02,
    0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00, 0x0f, 0x01, 0x00, 0x00, 0x00,
    0x48, 0x42, 0x00, 0x5c, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00,
    0x7f, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00, 0x7c, 0x01, 0x00,
    0x00, 0x00, 0x20, 0x42, 0x00, 0x57, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
    0x42, 0x00, 0x94, 0x07, 0x00, 0x00, 0x00, 0x03, 0x31, 0x30, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00};
static const char *const SUCCESS_CREATE_RESPONSE_UNIQUE_IDENTIFIER = "108";

static void _test_mongocrypt_kms_ctx_kmip_create(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt;
    mongocrypt_kms_ctx_t kms_ctx = {0};
    bool ok;
    mongocrypt_binary_t *bytes;
    _mongocrypt_buffer_t result;
    mongocrypt_status_t *status;
    _mongocrypt_endpoint_t *endpoint;

    status = mongocrypt_status_new();
    endpoint = _mongocrypt_endpoint_new("example.com", -1, NULL /* opts */, status);
    ASSERT_OK_STATUS(endpoint != NULL, status);

    crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
    ok = _mongocrypt_kms_ctx_init_kmip_create(&kms_ctx, endpoint, "kmip", &crypt->log);
    ASSERT_OK_STATUS(ok, kms_ctx.status);

    bytes = mongocrypt_binary_new();
    ok = mongocrypt_kms_ctx_message(&kms_ctx, bytes);
    ASSERT_OK_STATUS(ok, kms_ctx.status);

    ASSERT_CMPBYTES(CREATE_REQUEST,
                    sizeof(CREATE_REQUEST),
                    mongocrypt_binary_data(bytes),
                    mongocrypt_binary_len(bytes));
    ASSERT_OK(kms_ctx_feed_all(&kms_ctx, SUCCESS_CREATE_RESPONSE, sizeof(SUCCESS_CREATE_RESPONSE)), &kms_ctx);

    ok = _mongocrypt_kms_ctx_result(&kms_ctx, &result);
    ASSERT_OK_STATUS(ok, kms_ctx.status);
    ASSERT_STREQUAL((char *)result.data, SUCCESS_CREATE_RESPONSE_UNIQUE_IDENTIFIER);

    mongocrypt_binary_destroy(bytes);
    _mongocrypt_endpoint_destroy(endpoint);
    mongocrypt_status_destroy(status);
    _mongocrypt_kms_ctx_cleanup(&kms_ctx);
    mongocrypt_destroy(crypt);
}

/*
<RequestMessage tag="0x420078" type="Structure">
 <RequestHeader tag="0x420077" type="Structure">
  <ProtocolVersion tag="0x420069" type="Structure">
   <ProtocolVersionMajor tag="0x42006a" type="Integer" value="1"/>
   <ProtocolVersionMinor tag="0x42006b" type="Integer" value="2"/>
  </ProtocolVersion>
  <BatchCount tag="0x42000d" type="Integer" value="1"/>
 </RequestHeader>
 <BatchItem tag="0x42000f" type="Structure">
  <Operation tag="0x42005c" type="Enumeration" value="31"/>
  <RequestPayload tag="0x420079" type="Structure">
   <UniqueIdentifier tag="0x420094" type="TextString" value="111"/>
   <CryptographicParameters tag="0x42002b" type="Structure">
    <BlockCipherMode tag="0x420011" type="Enumeration" value="1"/>
    <PaddingMethod tag="0x42005f" type="Enumeration" value="3"/>
    <CryptographicAlgorithm tag="0x420028" type="Enumeration" value="3"/>
    <RandomIV tag="0x4200c5" type="Boolean" value="true"/>
   </CryptographicParameters>
   <Data tag="0x4200c2" type="ByteString"
value="6a4ede823ce8a7f62f361b398f224c5870d582dcc7bab5d9e58436dccc982d36a593b43a9925fae2e74b0f57bfb6bf5a17c7cc832bc2ba17ed3dfb93e00e47028d6ecf037c495922cf47aab8a93703c8a35332fab598a92ec78e191f6e5fe574"/>
  </RequestPayload>
 </BatchItem>
</RequestMessage>
*/
static const uint8_t ENCRYPT_REQUEST[] = {
    0x42, 0x00, 0x78, 0x01, 0x00, 0x00, 0x01, 0x20, 0x42, 0x00, 0x77, 0x01, 0x00, 0x00, 0x00, 0x38, 0x42, 0x00, 0x69,
    0x01, 0x00, 0x00, 0x00, 0x20, 0x42, 0x00, 0x6a, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
    0x00, 0x00, 0x42, 0x00, 0x6b, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x42,
    0x00, 0x0d, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00, 0x0f, 0x01,
    0x00, 0x00, 0x00, 0xd8, 0x42, 0x00, 0x5c, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00,
    0x00, 0x42, 0x00, 0x79, 0x01, 0x00, 0x00, 0x00, 0xc0, 0x42, 0x00, 0x94, 0x07, 0x00, 0x00, 0x00, 0x03, 0x31, 0x31,
    0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00, 0x2b, 0x01, 0x00, 0x00, 0x00, 0x40, 0x42, 0x00, 0x11, 0x05, 0x00,
    0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00, 0x5f, 0x05, 0x00, 0x00, 0x00, 0x04,
    0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00, 0x28, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
    0x03, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00, 0xc5, 0x06, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x01, 0x42, 0x00, 0xc2, 0x08, 0x00, 0x00, 0x00, 0x60, 0x6a, 0x4e, 0xde, 0x82, 0x3c, 0xe8, 0xa7, 0xf6, 0x2f,
    0x36, 0x1b, 0x39, 0x8f, 0x22, 0x4c, 0x58, 0x70, 0xd5, 0x82, 0xdc, 0xc7, 0xba, 0xb5, 0xd9, 0xe5, 0x84, 0x36, 0xdc,
    0xcc, 0x98, 0x2d, 0x36, 0xa5, 0x93, 0xb4, 0x3a, 0x99, 0x25, 0xfa, 0xe2, 0xe7, 0x4b, 0x0f, 0x57, 0xbf, 0xb6, 0xbf,
    0x5a, 0x17, 0xc7, 0xcc, 0x83, 0x2b, 0xc2, 0xba, 0x17, 0xed, 0x3d, 0xfb, 0x93, 0xe0, 0x0e, 0x47, 0x02, 0x8d, 0x6e,
    0xcf, 0x03, 0x7c, 0x49, 0x59, 0x22, 0xcf, 0x47, 0xaa, 0xb8, 0xa9, 0x37, 0x03, 0xc8, 0xa3, 0x53, 0x32, 0xfa, 0xb5,
    0x98, 0xa9, 0x2e, 0xc7, 0x8e, 0x19, 0x1f, 0x6e, 0x5f, 0xe5, 0x74

};
static const uint8_t PLAINTEXT[] = {0x6a, 0x4e, 0xde, 0x82, 0x3c, 0xe8, 0xa7, 0xf6, 0x2f, 0x36, 0x1b, 0x39, 0x8f, 0x22,
                                    0x4c, 0x58, 0x70, 0xd5, 0x82, 0xdc, 0xc7, 0xba, 0xb5, 0xd9, 0xe5, 0x84, 0x36, 0xdc,
                                    0xcc, 0x98, 0x2d, 0x36, 0xa5, 0x93, 0xb4, 0x3a, 0x99, 0x25, 0xfa, 0xe2, 0xe7, 0x4b,
                                    0x0f, 0x57, 0xbf, 0xb6, 0xbf, 0x5a, 0x17, 0xc7, 0xcc, 0x83, 0x2b, 0xc2, 0xba, 0x17,
                                    0xed, 0x3d, 0xfb, 0x93, 0xe0, 0x0e, 0x47, 0x02, 0x8d, 0x6e, 0xcf, 0x03, 0x7c, 0x49,
                                    0x59, 0x22, 0xcf, 0x47, 0xaa, 0xb8, 0xa9, 0x37, 0x03, 0xc8, 0xa3, 0x53, 0x32, 0xfa,
                                    0xb5, 0x98, 0xa9, 0x2e, 0xc7, 0x8e, 0x19, 0x1f, 0x6e, 0x5f, 0xe5, 0x74

};

static const char *const KEK_UNIQUE_IDENTIFIER = "111";

/*
<ResponseMessage tag="0x42007b" type="Structure">
 <ResponseHeader tag="0x42007a" type="Structure">
  <ProtocolVersion tag="0x420069" type="Structure">
   <ProtocolVersionMajor tag="0x42006a" type="Integer" value="1"/>
   <ProtocolVersionMinor tag="0x42006b" type="Integer" value="2"/>
  </ProtocolVersion>
  <TimeStamp tag="0x420092" type="DateTime" value="2024-01-04T21:11:03+0000"/>
  <BatchCount tag="0x42000d" type="Integer" value="1"/>
 </ResponseHeader>
 <BatchItem tag="0x42000f" type="Structure">
  <Operation tag="0x42005c" type="Enumeration" value="31"/><!-- Unknown -->
  <ResultStatus tag="0x42007f" type="Enumeration" value="0"/>
  <ResponsePayload tag="0x42007c" type="Structure">
   <UniqueIdentifier tag="0x420094" type="TextString" value="111"/>
   <Data tag="0x4200c2" type="ByteString"
value="e70c4fe3c4e67be8690ac0592369c4fdf11f44ebd456780d5613b78cec602c5a1299e0416c6728e227730d2bd186dccef1ee8c3a58520b2e4f6c1b4c46e1283b79b45cc5a87207f3ec8df539ae386337d305f791ed13fd7feb848edef961320375d19d8837e39a577dca3ad652830112"/>
   <IVCounterNonce tag="0x42003d" type="ByteString" value="2677c260edc35f736c233e741e18245f"/>
  </ResponsePayload>
 </BatchItem>
</ResponseMessage>
*/
static const uint8_t SUCCESS_ENCRYPT_RESPONSE[] = {
    0x42, 0x00, 0x7b, 0x01, 0x00, 0x00, 0x01, 0x20, 0x42, 0x00, 0x7a, 0x01, 0x00, 0x00, 0x00, 0x48, 0x42, 0x00, 0x69,
    0x01, 0x00, 0x00, 0x00, 0x20, 0x42, 0x00, 0x6a, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
    0x00, 0x00, 0x42, 0x00, 0x6b, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x42,
    0x00, 0x92, 0x09, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x65, 0x97, 0x2c, 0xb1, 0x42, 0x00, 0x0d, 0x02,
    0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00, 0x0f, 0x01, 0x00, 0x00, 0x00,
    0xc8, 0x42, 0x00, 0x5c, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00,
    0x7f, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00, 0x7c, 0x01, 0x00,
    0x00, 0x00, 0xa0, 0x42, 0x00, 0x94, 0x07, 0x00, 0x00, 0x00, 0x03, 0x31, 0x31, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x42, 0x00, 0xc2, 0x08, 0x00, 0x00, 0x00, 0x70, 0xe7, 0x0c, 0x4f, 0xe3, 0xc4, 0xe6, 0x7b, 0xe8, 0x69, 0x0a, 0xc0,
    0x59, 0x23, 0x69, 0xc4, 0xfd, 0xf1, 0x1f, 0x44, 0xeb, 0xd4, 0x56, 0x78, 0x0d, 0x56, 0x13, 0xb7, 0x8c, 0xec, 0x60,
    0x2c, 0x5a, 0x12, 0x99, 0xe0, 0x41, 0x6c, 0x67, 0x28, 0xe2, 0x27, 0x73, 0x0d, 0x2b, 0xd1, 0x86, 0xdc, 0xce, 0xf1,
    0xee, 0x8c, 0x3a, 0x58, 0x52, 0x0b, 0x2e, 0x4f, 0x6c, 0x1b, 0x4c, 0x46, 0xe1, 0x28, 0x3b, 0x79, 0xb4, 0x5c, 0xc5,
    0xa8, 0x72, 0x07, 0xf3, 0xec, 0x8d, 0xf5, 0x39, 0xae, 0x38, 0x63, 0x37, 0xd3, 0x05, 0xf7, 0x91, 0xed, 0x13, 0xfd,
    0x7f, 0xeb, 0x84, 0x8e, 0xde, 0xf9, 0x61, 0x32, 0x03, 0x75, 0xd1, 0x9d, 0x88, 0x37, 0xe3, 0x9a, 0x57, 0x7d, 0xca,
    0x3a, 0xd6, 0x52, 0x83, 0x01, 0x12, 0x42, 0x00, 0x3d, 0x08, 0x00, 0x00, 0x00, 0x10, 0x26, 0x77, 0xc2, 0x60, 0xed,
    0xc3, 0x5f, 0x73, 0x6c, 0x23, 0x3e, 0x74, 0x1e, 0x18, 0x24, 0x5f};

/* Encrypted data with server-generated IV prepended */
static const uint8_t IV_CIPHERTEXT[] = {
    0x26, 0x77, 0xc2, 0x60, 0xed, 0xc3, 0x5f, 0x73, 0x6c, 0x23, 0x3e, 0x74, 0x1e, 0x18, 0x24, 0x5f, 0xe7, 0x0c, 0x4f,
    0xe3, 0xc4, 0xe6, 0x7b, 0xe8, 0x69, 0x0a, 0xc0, 0x59, 0x23, 0x69, 0xc4, 0xfd, 0xf1, 0x1f, 0x44, 0xeb, 0xd4, 0x56,
    0x78, 0x0d, 0x56, 0x13, 0xb7, 0x8c, 0xec, 0x60, 0x2c, 0x5a, 0x12, 0x99, 0xe0, 0x41, 0x6c, 0x67, 0x28, 0xe2, 0x27,
    0x73, 0x0d, 0x2b, 0xd1, 0x86, 0xdc, 0xce, 0xf1, 0xee, 0x8c, 0x3a, 0x58, 0x52, 0x0b, 0x2e, 0x4f, 0x6c, 0x1b, 0x4c,
    0x46, 0xe1, 0x28, 0x3b, 0x79, 0xb4, 0x5c, 0xc5, 0xa8, 0x72, 0x07, 0xf3, 0xec, 0x8d, 0xf5, 0x39, 0xae, 0x38, 0x63,
    0x37, 0xd3, 0x05, 0xf7, 0x91, 0xed, 0x13, 0xfd, 0x7f, 0xeb, 0x84, 0x8e, 0xde, 0xf9, 0x61, 0x32, 0x03, 0x75, 0xd1,
    0x9d, 0x88, 0x37, 0xe3, 0x9a, 0x57, 0x7d, 0xca, 0x3a, 0xd6, 0x52, 0x83, 0x01, 0x12

};

static void _test_mongocrypt_kms_ctx_kmip_encrypt(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt;
    mongocrypt_kms_ctx_t kms_ctx = {0};
    bool ok;
    mongocrypt_binary_t *bytes;
    _mongocrypt_buffer_t result;
    mongocrypt_status_t *status;
    _mongocrypt_endpoint_t *endpoint;

    _mongocrypt_buffer_t plaintext = {0};
    ASSERT(_mongocrypt_buffer_copy_from_data_and_size(&plaintext, PLAINTEXT, sizeof(PLAINTEXT)));

    status = mongocrypt_status_new();
    endpoint = _mongocrypt_endpoint_new("example.com", -1, NULL /* opts */, status);
    ASSERT_OK_STATUS(endpoint != NULL, status);

    crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
    ok = _mongocrypt_kms_ctx_init_kmip_encrypt(&kms_ctx,
                                               endpoint,
                                               (char *)KEK_UNIQUE_IDENTIFIER,
                                               "kmip",
                                               &plaintext,
                                               &crypt->log);
    ASSERT_OK_STATUS(ok, kms_ctx.status);

    bytes = mongocrypt_binary_new();
    ok = mongocrypt_kms_ctx_message(&kms_ctx, bytes);
    ASSERT_OK_STATUS(ok, kms_ctx.status);

    ASSERT_CMPBYTES(ENCRYPT_REQUEST,
                    sizeof(ENCRYPT_REQUEST),
                    mongocrypt_binary_data(bytes),
                    mongocrypt_binary_len(bytes));
    ASSERT_OK(kms_ctx_feed_all(&kms_ctx, SUCCESS_ENCRYPT_RESPONSE, sizeof(SUCCESS_ENCRYPT_RESPONSE)), &kms_ctx);

    ok = _mongocrypt_kms_ctx_result(&kms_ctx, &result);
    ASSERT_OK_STATUS(ok, kms_ctx.status);
    ASSERT_CMPBYTES(result.data, result.len, IV_CIPHERTEXT, sizeof(IV_CIPHERTEXT));

    _mongocrypt_buffer_cleanup(&plaintext);
    mongocrypt_binary_destroy(bytes);
    _mongocrypt_endpoint_destroy(endpoint);
    mongocrypt_status_destroy(status);
    _mongocrypt_kms_ctx_cleanup(&kms_ctx);
    mongocrypt_destroy(crypt);
}

/*
<RequestMessage tag="0x420078" type="Structure">
 <RequestHeader tag="0x420077" type="Structure">
  <ProtocolVersion tag="0x420069" type="Structure">
   <ProtocolVersionMajor tag="0x42006a" type="Integer" value="1"/>
   <ProtocolVersionMinor tag="0x42006b" type="Integer" value="2"/>
  </ProtocolVersion>
  <BatchCount tag="0x42000d" type="Integer" value="1"/>
 </RequestHeader>
 <BatchItem tag="0x42000f" type="Structure">
  <Operation tag="0x42005c" type="Enumeration" value="32"/><!-- Unknown -->
  <RequestPayload tag="0x420079" type="Structure">
   <UniqueIdentifier tag="0x420094" type="TextString" value="111"/>
   <CryptographicParameters tag="0x42002b" type="Structure">
    <BlockCipherMode tag="0x420011" type="Enumeration" value="1"/>
    <PaddingMethod tag="0x42005f" type="Enumeration" value="3"/>
    <CryptographicAlgorithm tag="0x420028" type="Enumeration" value="3"/>
   </CryptographicParameters>
   <Data tag="0x4200c2" type="ByteString"
value="e70c4fe3c4e67be8690ac0592369c4fdf11f44ebd456780d5613b78cec602c5a1299e0416c6728e227730d2bd186dccef1ee8c3a58520b2e4f6c1b4c46e1283b79b45cc5a87207f3ec8df539ae386337d305f791ed13fd7feb848edef961320375d19d8837e39a577dca3ad652830112"/>
   <IVCounterNonce tag="0x42003d" type="ByteString" value="2677c260edc35f736c233e741e18245f"/>
  </RequestPayload>
 </BatchItem>
</RequestMessage>
*/
static const uint8_t DECRYPT_REQUEST[] = {
    0x42, 0x00, 0x78, 0x01, 0x00, 0x00, 0x01, 0x38, 0x42, 0x00, 0x77, 0x01, 0x00, 0x00, 0x00, 0x38, 0x42, 0x00, 0x69,
    0x01, 0x00, 0x00, 0x00, 0x20, 0x42, 0x00, 0x6a, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
    0x00, 0x00, 0x42, 0x00, 0x6b, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x42,
    0x00, 0x0d, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00, 0x0f, 0x01,
    0x00, 0x00, 0x00, 0xf0, 0x42, 0x00, 0x5c, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
    0x00, 0x42, 0x00, 0x79, 0x01, 0x00, 0x00, 0x00, 0xd8, 0x42, 0x00, 0x94, 0x07, 0x00, 0x00, 0x00, 0x03, 0x31, 0x31,
    0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00, 0x2b, 0x01, 0x00, 0x00, 0x00, 0x30, 0x42, 0x00, 0x11, 0x05, 0x00,
    0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00, 0x5f, 0x05, 0x00, 0x00, 0x00, 0x04,
    0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00, 0x28, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
    0x03, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00, 0xc2, 0x08, 0x00, 0x00, 0x00, 0x70, 0xe7, 0x0c, 0x4f, 0xe3, 0xc4, 0xe6,
    0x7b, 0xe8, 0x69, 0x0a, 0xc0, 0x59, 0x23, 0x69, 0xc4, 0xfd, 0xf1, 0x1f, 0x44, 0xeb, 0xd4, 0x56, 0x78, 0x0d, 0x56,
    0x13, 0xb7, 0x8c, 0xec, 0x60, 0x2c, 0x5a, 0x12, 0x99, 0xe0, 0x41, 0x6c, 0x67, 0x28, 0xe2, 0x27, 0x73, 0x0d, 0x2b,
    0xd1, 0x86, 0xdc, 0xce, 0xf1, 0xee, 0x8c, 0x3a, 0x58, 0x52, 0x0b, 0x2e, 0x4f, 0x6c, 0x1b, 0x4c, 0x46, 0xe1, 0x28,
    0x3b, 0x79, 0xb4, 0x5c, 0xc5, 0xa8, 0x72, 0x07, 0xf3, 0xec, 0x8d, 0xf5, 0x39, 0xae, 0x38, 0x63, 0x37, 0xd3, 0x05,
    0xf7, 0x91, 0xed, 0x13, 0xfd, 0x7f, 0xeb, 0x84, 0x8e, 0xde, 0xf9, 0x61, 0x32, 0x03, 0x75, 0xd1, 0x9d, 0x88, 0x37,
    0xe3, 0x9a, 0x57, 0x7d, 0xca, 0x3a, 0xd6, 0x52, 0x83, 0x01, 0x12, 0x42, 0x00, 0x3d, 0x08, 0x00, 0x00, 0x00, 0x10,
    0x26, 0x77, 0xc2, 0x60, 0xed, 0xc3, 0x5f, 0x73, 0x6c, 0x23, 0x3e, 0x74, 0x1e, 0x18, 0x24, 0x5f

};

/*
<ResponseMessage tag="0x42007b" type="Structure">
 <ResponseHeader tag="0x42007a" type="Structure">
  <ProtocolVersion tag="0x420069" type="Structure">
   <ProtocolVersionMajor tag="0x42006a" type="Integer" value="1"/>
   <ProtocolVersionMinor tag="0x42006b" type="Integer" value="2"/>
  </ProtocolVersion>
  <TimeStamp tag="0x420092" type="DateTime" value="2024-01-04T22:20:54+0000"/>
  <BatchCount tag="0x42000d" type="Integer" value="1"/>
 </ResponseHeader>
 <BatchItem tag="0x42000f" type="Structure">
  <Operation tag="0x42005c" type="Enumeration" value="32"/><!-- Unknown -->
  <ResultStatus tag="0x42007f" type="Enumeration" value="0"/>
  <ResponsePayload tag="0x42007c" type="Structure">
   <UniqueIdentifier tag="0x420094" type="TextString" value="111"/>
   <Data tag="0x4200c2" type="ByteString"
value="6a4ede823ce8a7f62f361b398f224c5870d582dcc7bab5d9e58436dccc982d36a593b43a9925fae2e74b0f57bfb6bf5a17c7cc832bc2ba17ed3dfb93e00e47028d6ecf037c495922cf47aab8a93703c8a35332fab598a92ec78e191f6e5fe574"/>
  </ResponsePayload>
 </BatchItem>
</ResponseMessage>
*/
static const uint8_t SUCCESS_DECRYPT_RESPONSE[] = {
    0x42, 0x00, 0x7b, 0x01, 0x00, 0x00, 0x00, 0xf8, 0x42, 0x00, 0x7a, 0x01, 0x00, 0x00, 0x00, 0x48, 0x42, 0x00, 0x69,
    0x01, 0x00, 0x00, 0x00, 0x20, 0x42, 0x00, 0x6a, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
    0x00, 0x00, 0x42, 0x00, 0x6b, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x42,
    0x00, 0x92, 0x09, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x65, 0x97, 0x2f, 0x46, 0x42, 0x00, 0x0d, 0x02,
    0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00, 0x0f, 0x01, 0x00, 0x00, 0x00,
    0xa0, 0x42, 0x00, 0x5c, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00,
    0x7f, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00, 0x7c, 0x01, 0x00,
    0x00, 0x00, 0x78, 0x42, 0x00, 0x94, 0x07, 0x00, 0x00, 0x00, 0x03, 0x31, 0x31, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x42, 0x00, 0xc2, 0x08, 0x00, 0x00, 0x00, 0x60, 0x6a, 0x4e, 0xde, 0x82, 0x3c, 0xe8, 0xa7, 0xf6, 0x2f, 0x36, 0x1b,
    0x39, 0x8f, 0x22, 0x4c, 0x58, 0x70, 0xd5, 0x82, 0xdc, 0xc7, 0xba, 0xb5, 0xd9, 0xe5, 0x84, 0x36, 0xdc, 0xcc, 0x98,
    0x2d, 0x36, 0xa5, 0x93, 0xb4, 0x3a, 0x99, 0x25, 0xfa, 0xe2, 0xe7, 0x4b, 0x0f, 0x57, 0xbf, 0xb6, 0xbf, 0x5a, 0x17,
    0xc7, 0xcc, 0x83, 0x2b, 0xc2, 0xba, 0x17, 0xed, 0x3d, 0xfb, 0x93, 0xe0, 0x0e, 0x47, 0x02, 0x8d, 0x6e, 0xcf, 0x03,
    0x7c, 0x49, 0x59, 0x22, 0xcf, 0x47, 0xaa, 0xb8, 0xa9, 0x37, 0x03, 0xc8, 0xa3, 0x53, 0x32, 0xfa, 0xb5, 0x98, 0xa9,
    0x2e, 0xc7, 0x8e, 0x19, 0x1f, 0x6e, 0x5f, 0xe5, 0x74};

static void _test_mongocrypt_kms_ctx_kmip_decrypt(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt;
    mongocrypt_kms_ctx_t kms_ctx = {0};
    bool ok;
    mongocrypt_binary_t *bytes;
    _mongocrypt_buffer_t result;
    mongocrypt_status_t *status;
    _mongocrypt_endpoint_t *endpoint;

    /* should be _mongocrypt_key_doc_t key_doc = {0}
     * but doesn't compile on Ubuntu 16 due to https://bugs.llvm.org/show_bug.cgi?id=21629 */
    _mongocrypt_key_doc_t key_doc;
    memset(&key_doc, 0, sizeof(key_doc));

    key_doc.kek.kms_provider = MONGOCRYPT_KMS_PROVIDER_KMIP;
    key_doc.kek.provider.kmip.delegated = true;
    key_doc.kek.provider.kmip.key_id = (char *)KEK_UNIQUE_IDENTIFIER;
    ASSERT(_mongocrypt_buffer_copy_from_data_and_size(&key_doc.key_material, IV_CIPHERTEXT, sizeof(IV_CIPHERTEXT)));

    status = mongocrypt_status_new();
    endpoint = _mongocrypt_endpoint_new("example.com", -1, NULL /* opts */, status);
    ASSERT_OK_STATUS(endpoint != NULL, status);

    crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
    ok = _mongocrypt_kms_ctx_init_kmip_decrypt(&kms_ctx, endpoint, "kmip", &key_doc, &crypt->log);
    ASSERT_OK_STATUS(ok, kms_ctx.status);

    bytes = mongocrypt_binary_new();
    ok = mongocrypt_kms_ctx_message(&kms_ctx, bytes);
    ASSERT_OK_STATUS(ok, kms_ctx.status);

    ASSERT_CMPBYTES(DECRYPT_REQUEST,
                    sizeof(DECRYPT_REQUEST),
                    mongocrypt_binary_data(bytes),
                    mongocrypt_binary_len(bytes));
    ASSERT_OK(kms_ctx_feed_all(&kms_ctx, SUCCESS_DECRYPT_RESPONSE, sizeof(SUCCESS_DECRYPT_RESPONSE)), &kms_ctx);

    ok = _mongocrypt_kms_ctx_result(&kms_ctx, &result);
    ASSERT_OK_STATUS(ok, kms_ctx.status);
    ASSERT_CMPBYTES(result.data, result.len, PLAINTEXT, sizeof(PLAINTEXT));

    _mongocrypt_buffer_cleanup(&key_doc.key_material);
    mongocrypt_binary_destroy(bytes);
    _mongocrypt_endpoint_destroy(endpoint);
    mongocrypt_status_destroy(status);
    _mongocrypt_kms_ctx_cleanup(&kms_ctx);
    mongocrypt_destroy(crypt);
}

static void _test_mongocrypt_kms_ctx_get_kms_provider(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt;
    mongocrypt_kms_ctx_t kms_ctx = {0};
    bool ok;
    mongocrypt_status_t *status;
    _mongocrypt_endpoint_t *endpoint;
    uint32_t len;

    status = mongocrypt_status_new();
    endpoint = _mongocrypt_endpoint_new("example.com", -1, NULL /* opts */, status);
    ASSERT_OK_STATUS(endpoint != NULL, status);

    crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
    ok = _mongocrypt_kms_ctx_init_kmip_activate(&kms_ctx,
                                                endpoint,
                                                (char *)SUCCESS_ACTIVATE_RESPONSE_UNIQUE_IDENTIFIER,
                                                "kmip",
                                                &crypt->log);
    ASSERT_OK_STATUS(ok, kms_ctx.status);

    ASSERT_STREQUAL(mongocrypt_kms_ctx_get_kms_provider(&kms_ctx, &len), "kmip");
    ASSERT_CMPINT(len, ==, 4);

    _mongocrypt_endpoint_destroy(endpoint);
    mongocrypt_status_destroy(status);
    _mongocrypt_kms_ctx_cleanup(&kms_ctx);
    mongocrypt_destroy(crypt);
}

static void _test_mongocrypt_kms_ctx_default_port(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt;
    mongocrypt_kms_ctx_t kms_ctx = {0};
    mongocrypt_status_t *status;
    _mongocrypt_endpoint_t *endpoint;
    const char *kms_ctx_endpoint;

    crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
    status = mongocrypt_status_new();

    /* Test an endpoint with no port. */
    endpoint = _mongocrypt_endpoint_new("example.com", -1, NULL /* opts */, status);
    ASSERT_OK_STATUS(endpoint != NULL, status);

    /* Test a KMIP request. Expect a default of 5696. */
    ASSERT_OK(_mongocrypt_kms_ctx_init_kmip_activate(&kms_ctx,
                                                     endpoint,
                                                     (char *)SUCCESS_ACTIVATE_RESPONSE_UNIQUE_IDENTIFIER,
                                                     "kmip",
                                                     &crypt->log),
              &kms_ctx);
    ASSERT_OK(mongocrypt_kms_ctx_endpoint(&kms_ctx, &kms_ctx_endpoint), &kms_ctx);
    ASSERT_STREQUAL("example.com:5696", kms_ctx_endpoint);
    _mongocrypt_kms_ctx_cleanup(&kms_ctx);
    _mongocrypt_endpoint_destroy(endpoint);

    /* Test an endpoint with a custom port. */
    endpoint = _mongocrypt_endpoint_new("example.com:1234", -1, NULL /* opts */, status);
    ASSERT_OK_STATUS(endpoint != NULL, status);

    /* Test a KMIP request. Expect the custom port to be retained. */
    ASSERT_OK(_mongocrypt_kms_ctx_init_kmip_activate(&kms_ctx,
                                                     endpoint,
                                                     (char *)SUCCESS_ACTIVATE_RESPONSE_UNIQUE_IDENTIFIER,
                                                     "kmip",
                                                     &crypt->log),
              &kms_ctx);
    ASSERT_OK(mongocrypt_kms_ctx_endpoint(&kms_ctx, &kms_ctx_endpoint), &kms_ctx);
    ASSERT_STREQUAL("example.com:1234", kms_ctx_endpoint);
    _mongocrypt_kms_ctx_cleanup(&kms_ctx);
    _mongocrypt_endpoint_destroy(endpoint);

    mongocrypt_destroy(crypt);
    mongocrypt_status_destroy(status);
}

static void _test_mongocrypt_kms_ctx_feed_empty_bytes(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt;
    mongocrypt_kms_ctx_t kms_ctx = {0};
    mongocrypt_status_t *status;
    _mongocrypt_endpoint_t *endpoint;
    mongocrypt_binary_t *bytes = mongocrypt_binary_new();

    crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
    status = mongocrypt_status_new();

    endpoint = _mongocrypt_endpoint_new("example.com", -1, NULL /* opts */, status);
    ASSERT_OK_STATUS(endpoint != NULL, status);

    ASSERT_OK(_mongocrypt_kms_ctx_init_kmip_activate(&kms_ctx,
                                                     endpoint,
                                                     (char *)SUCCESS_ACTIVATE_RESPONSE_UNIQUE_IDENTIFIER,
                                                     "kmip",
                                                     &crypt->log),
              &kms_ctx);

    /* Test KMS Feed. Expect to fail with empty bytes */
    ASSERT_FAILS(mongocrypt_kms_ctx_feed(&kms_ctx, bytes), &kms_ctx, "argument 'bytes' cannot be empty");

    mongocrypt_binary_destroy(bytes);
    _mongocrypt_kms_ctx_cleanup(&kms_ctx);
    _mongocrypt_endpoint_destroy(endpoint);
    mongocrypt_destroy(crypt);
    mongocrypt_status_destroy(status);
}

// _test_mongocrypt_kms_ctx_kmip_response_bad_length tests KMIP responses that report bad lengths.
static void _test_mongocrypt_kms_ctx_kmip_response_bad_length(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
    bool ok;
    uint8_t secretdata[KMS_KMIP_REQUEST_SECRETDATA_LENGTH] = {0};
    mongocrypt_status_t *status = mongocrypt_status_new();
    _mongocrypt_endpoint_t *endpoint = _mongocrypt_endpoint_new("example.com", -1, NULL /* opts */, status);
    ASSERT_OK_STATUS(endpoint != NULL, status);

    // Regression test for MONGOCRYPT-909: test UINT32_MAX
    {
        mongocrypt_kms_ctx_t kms_ctx = {0};
        static const char *kmip_header = "\x42\x00\x7b\x01"  // Tag=ResponseMessage, Type=Structure
                                         "\xff\xff\xff\xff"; // Length = UINT32_MAX

        ok = _mongocrypt_kms_ctx_init_kmip_register(&kms_ctx,
                                                    endpoint,
                                                    secretdata,
                                                    KMS_KMIP_REQUEST_SECRETDATA_LENGTH,
                                                    "kmip",
                                                    &crypt->log);
        ASSERT_OK_STATUS(ok, kms_ctx.status);

        // Parser requests 8 bytes for initial header:
        ASSERT_CMPINT(mongocrypt_kms_ctx_bytes_needed(&kms_ctx), ==, 8u);

        mongocrypt_binary_t *bytes = mongocrypt_binary_new_from_data((uint8_t *)kmip_header, 8u);

        // Expect error (not abort):
        ASSERT_FAILS(mongocrypt_kms_ctx_feed(&kms_ctx, bytes), &kms_ctx, "KMS KMIP response too large");

        mongocrypt_binary_destroy(bytes);
        _mongocrypt_kms_ctx_cleanup(&kms_ctx);
    }

    // Length reported is less than data fed.
    {
        mongocrypt_kms_ctx_t kms_ctx = {0};
        static const char *kmip_header = "\x42\x00\x7b\x01"  // Tag=ResponseMessage, Type=Structure
                                         "\x00\x00\x00\x01"; // Length = 1
        ASSERT_OK_STATUS(endpoint != NULL, status);

        ok = _mongocrypt_kms_ctx_init_kmip_register(&kms_ctx,
                                                    endpoint,
                                                    secretdata,
                                                    KMS_KMIP_REQUEST_SECRETDATA_LENGTH,
                                                    "kmip",
                                                    &crypt->log);
        ASSERT_OK_STATUS(ok, kms_ctx.status);

        // Parser requests 8 bytes for initial header:
        ASSERT_CMPINT(mongocrypt_kms_ctx_bytes_needed(&kms_ctx), ==, 8u);

        mongocrypt_binary_t *bytes = mongocrypt_binary_new_from_data((uint8_t *)kmip_header, 8u);
        ASSERT_OK(mongocrypt_kms_ctx_feed(&kms_ctx, bytes), &kms_ctx);

        ASSERT_CMPINT(mongocrypt_kms_ctx_bytes_needed(&kms_ctx), ==, 1u);
        // Attempting to feed more data errors.
        ASSERT_FAILS(mongocrypt_kms_ctx_feed(&kms_ctx, bytes), &kms_ctx, "KMS response fed too much data");

        mongocrypt_binary_destroy(bytes);
        _mongocrypt_kms_ctx_cleanup(&kms_ctx);
    }

    // Length reported is zero.
    {
        mongocrypt_kms_ctx_t kms_ctx = {0};
        static const char *kmip_header = "\x42\x00\x7b\x01"  // Tag=ResponseMessage, Type=Structure
                                         "\x00\x00\x00\x00"; // Length = 0
        ASSERT_OK_STATUS(endpoint != NULL, status);

        ok = _mongocrypt_kms_ctx_init_kmip_register(&kms_ctx,
                                                    endpoint,
                                                    secretdata,
                                                    KMS_KMIP_REQUEST_SECRETDATA_LENGTH,
                                                    "kmip",
                                                    &crypt->log);
        ASSERT_OK_STATUS(ok, kms_ctx.status);

        // Parser requests 8 bytes for initial header:
        ASSERT_CMPINT(mongocrypt_kms_ctx_bytes_needed(&kms_ctx), ==, 8u);

        mongocrypt_binary_t *bytes = mongocrypt_binary_new_from_data((uint8_t *)kmip_header, 8u);
        ASSERT_FAILS(mongocrypt_kms_ctx_feed(&kms_ctx, bytes), &kms_ctx, "Error getting UniqueIdentifer");

        mongocrypt_binary_destroy(bytes);
        _mongocrypt_kms_ctx_cleanup(&kms_ctx);
    }

    _mongocrypt_endpoint_destroy(endpoint);
    mongocrypt_status_destroy(status);
    mongocrypt_destroy(crypt);
}

void _mongocrypt_tester_install_kms_ctx(_mongocrypt_tester_t *tester) {
    INSTALL_TEST(_test_mongocrypt_kms_ctx_kmip_register);
    INSTALL_TEST(_test_mongocrypt_kms_ctx_kmip_activate);
    INSTALL_TEST(_test_mongocrypt_kms_ctx_kmip_get);
    INSTALL_TEST(_test_mongocrypt_kms_ctx_kmip_create);
    INSTALL_TEST(_test_mongocrypt_kms_ctx_kmip_encrypt);
    INSTALL_TEST(_test_mongocrypt_kms_ctx_kmip_decrypt);
    INSTALL_TEST(_test_mongocrypt_kms_ctx_get_kms_provider);
    INSTALL_TEST(_test_mongocrypt_kms_ctx_default_port);
    INSTALL_TEST(_test_mongocrypt_kms_ctx_feed_empty_bytes);
    INSTALL_TEST(_test_mongocrypt_kms_ctx_kmip_response_bad_length);
}
