/*
 * 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-private.h"
#include "test-mongocrypt.h"
#include "test-mongocrypt-util.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">
   </TemplateAttribute>
   <SecretData tag="0x420085" type="Structure">
    <SecretDataType tag="0x420086" type="Enumeration" value="2"/>
    <KeyBlock tag="0x420040" type="Structure">
     <KeyFormatType tag="0x420042" type="Enumeration" value="1"/>
     <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, 0x18, 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, 0xd0, 0x42, 0x00, 0x5c, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
   0x03, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00, 0x79, 0x01, 0x00, 0x00, 0x00, 0xb8,
   0x42, 0x00, 0x57, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x07, 0x00,
   0x00, 0x00, 0x00, 0x42, 0x00, 0x91, 0x01, 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, 0x01, 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,
      &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,
      &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, &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);
}

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,
      &crypt->log);
   ASSERT_OK_STATUS (ok, kms_ctx.status);

   ASSERT_STREQUAL (mongocrypt_kms_ctx_get_kms_provider (&kms_ctx, NULL),
                    "kmip");

   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,
                 &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,
                 &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,
                 &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);
}

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_get_kms_provider);
   INSTALL_TEST (_test_mongocrypt_kms_ctx_default_port);
   INSTALL_TEST (_test_mongocrypt_kms_ctx_feed_empty_bytes);
}
